upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/docs/derivation.md
diff options
context:
space:
mode:
authorbitflash-coin <bookofshamers@outlook.com>2026-08-04 06:34:32 -0700
committerbitflash-coin <bookofshamers@outlook.com>2026-08-04 06:34:32 -0700
commit5ce650382352c1503f40abf08129ada69402af55 (patch)
tree6a18198901f1e2c4933f1ac53c9365eec512bf63 /docs/derivation.md
parentf34113291da7e5e3dc7d2468d4f9c212c05c584b (diff)
public commit
Diffstat (limited to 'docs/derivation.md')
-rw-r--r--docs/derivation.md125
1 files changed, 125 insertions, 0 deletions
diff --git a/docs/derivation.md b/docs/derivation.md
new file mode 100644
index 0000000..c5ee3ad
--- /dev/null
+++ b/docs/derivation.md
@@ -0,0 +1,125 @@
1# Key derivation and address format
2
3This document exists so that a Bitflash wallet can be rebuilt **without this
4software**. If the project stops, the binaries are lost, or you simply do not
5trust them, the twelve words plus what is written here are enough to recover
6your private keys with ordinary cryptographic tools.
7
8Everything below is verified by `docs/verify-derivation.py`, a self-contained
9script that implements this document from scratch — no Bitflash code, no
10libraries beyond Python's standard library — and checks that it reproduces the
11addresses the wallet produces. If the two ever disagree, this document is wrong
12and the script will say so.
13
14## Addresses
15
16An address is the Base58Check encoding of a version byte followed by
17`RIPEMD160(SHA256(public key))`.
18
19| | |
20|---|---|
21| curve | secp256k1 |
22| public key encoding | **uncompressed**, 65 bytes, `0x04 ‖ X ‖ Y` |
23| address version byte | **25** (`0x19`), which is why addresses begin with `B` |
24| checksum | first 4 bytes of `SHA256(SHA256(payload))`, appended before Base58 |
25
26The public key is uncompressed. This matters more than anything else here: the
27same private key hashed in compressed form gives a completely different, valid
28looking address that holds nothing. Bitflash descends from Bitcoin 0.1.0, which
29predates compressed keys.
30
31Outputs paying an address use the usual script:
32
33```
34OP_DUP OP_HASH160 <hash160> OP_EQUALVERIFY OP_CHECKSIG
35```
36
37Mined coins and payments made directly to a public key use the older form
38`<pubkey> OP_CHECKSIG`. A wallet scanning the chain must recognise both.
39
40## From twelve words to a master key
41
42Standard BIP-39 and BIP-32, with no modifications:
43
441. **Seed.** `PBKDF2-HMAC-SHA512(password = mnemonic, salt = "mnemonic" + passphrase, iterations = 2048, length = 64 bytes)`. Bitflash always uses an empty passphrase, so the salt is the literal string `mnemonic`.
452. **Master key.** `I = HMAC-SHA512(key = "Bitcoin seed", data = seed)`. The master private key is `I[0..32]`, the master chain code is `I[32..64]`.
46
47Child derivation is BIP-32 `CKDpriv`, hardened and non-hardened as usual.
48
49## Two layouts
50
51A wallet records which layout it uses in `wallet.dat` under the key `hdschema`.
52Wallets created before that field existed are treated as legacy.
53
54### Legacy (`hdschema` absent or 1)
55
56```
57m/i'
58```
59
60Hardened children of the master key, one flat sequence. Index 0 is the wallet's
61default receiving address; the key pool and change take the following indices.
62
63Used by every wallet created before BIP-44 support existed, and kept forever:
64those phrases must keep working.
65
66### BIP-44 (`hdschema` = 2)
67
68```
69m/44'/4346950'/0'/0/i external chain (receiving)
70m/44'/4346950'/0'/1/i internal chain (change)
71```
72
73Coin type **4346950** is `0x425446`, the ASCII bytes `BTF`. It is
74[registered for Bitflash under the symbol BITFLASH](https://github.com/satoshilabs/slips/blob/master/slip-0044.md).
75The account level is fixed at `0'`; Bitflash does not expose multiple accounts.
76
77A wallet restored from a phrase scans **both** layouts, because the words alone
78do not say which one produced them.
79
80## Test vectors
81
82Using the public BIP-39 test mnemonic:
83
84```
85abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about
86```
87
88### Legacy
89
90| path | address |
91|---|---|
92| `m/0'` | `BHJNmVvkVv59fgn7gmgP78BtciQi2HY8Hy` |
93| `m/1'` | `B5qh9opG8sH8Mv9k2xzHoTeSUd9BkT6XEZ` |
94| `m/2'` | `BCNH8uGyvkiz3uXjhA9iiF27PWAaTStNin` |
95| `m/3'` | `BC1QNaXuTPGDgLZy53PghZMHEmZcp5GLgD` |
96
97### BIP-44 external chain
98
99| path | address |
100|---|---|
101| `m/44'/4346950'/0'/0/0` | `BF1ukigB1afmqxH64gJymPwUqFBrRqWbrb` |
102| `m/44'/4346950'/0'/0/1` | `BACY8pcuCapNQr897eRHroNrgN38GSsEzV` |
103| `m/44'/4346950'/0'/0/2` | `BMzkMN2k1phNBqy8RPb4Pv1i2QpwsWkKPa` |
104| `m/44'/4346950'/0'/0/3` | `BNPZ8U6FHXr9Ererk8psyM2RqEVtF6sfBQ` |
105
106Check them yourself:
107
108```
109python docs/verify-derivation.py
110```
111
112Both tables were produced by the wallet — the legacy one by the v1.2.15 binary,
113which only knows that layout — and then reproduced independently by the script.
114
115## What this document does not give you
116
117Deriving the keys is not the same as spending. To move recovered coins you still
118need a node that knows the network: the chain, the peer protocol, and the
119transaction format. This document is about **not losing the keys**, which is the
120part that cannot be reconstructed later by reading source code that no longer
121exists anywhere.
122
123Note also that `wallet.dat` stores private keys unencrypted. A recovery phrase
124protects you against losing the file; it does not protect the file. Anyone who
125can read it can spend the coins.