DILSHAD737 commited on
Commit
5857392
·
1 Parent(s): a2e6beb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +214 -6
app.py CHANGED
@@ -1,9 +1,217 @@
1
- # test_index.py
 
 
2
  from eth_account import Account
 
 
3
 
4
- Account.enable_unaudited_hdwallet_features()
5
- phrase = "lesson cushion crane affair federal decline limb spend reunion chief cart zebra"
 
 
 
6
 
7
- for idx in range(10):
8
- acct = Account.from_mnemonic(phrase, account_path=f"m/44'/60'/0'/0/{idx}")
9
- print(f"Index {idx}: {acct.address}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from itertools import permutations, combinations
2
+ import hashlib, hmac, os, time, math, gc
3
+ from multiprocessing import Pool
4
  from eth_account import Account
5
+ import coincurve
6
+ from eth_utils import keccak, to_checksum_address
7
 
8
+ # ============================================================
9
+ words = [
10
+ "lesson", "cushion", "crane", "affair", "federal",
11
+ "decline", "limb", "spend", "reunion", "chief", "cart", "zebra",
12
+ ]
13
 
14
+ PHRASE_MODE = "replace"
15
+ USE_BIP39 = True
16
+ BIP39_PASSPHRASE = ""
17
+ HD_DERIVATION = True
18
+ ADDRESS_INDEX = 0
19
+ CHECK_BIP39_CHECKSUM = True
20
+ ADDRESSES_FILE = "12730802_addresses.csv"
21
+ MATCHED_FILE = f"matched_keys_{PHRASE_MODE}.txt"
22
+ BATCH_SIZE = 5000
23
+ NUM_WORKERS = 96
24
+ # ============================================================
25
+
26
+
27
+ # ---------- secp256k1 ----------
28
+ _SECP256K1_N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
29
+ _HARDENED = 0x80000000
30
+
31
+
32
+ def _bip32_derive_child_priv(parent_priv, parent_chain, index):
33
+ if index >= _HARDENED:
34
+ data = b'\x00' + parent_priv + index.to_bytes(4, 'big')
35
+ else:
36
+ pub = coincurve.PrivateKey(parent_priv).public_key.format(compressed=True)
37
+ data = pub + index.to_bytes(4, 'big')
38
+ I = hmac.new(parent_chain, data, hashlib.sha512).digest()
39
+ child_int = (int.from_bytes(I[:32], 'big') + int.from_bytes(parent_priv, 'big')) % _SECP256K1_N
40
+ if child_int == 0:
41
+ raise ValueError("Invalid child key")
42
+ return child_int.to_bytes(32, 'big'), I[32:]
43
+
44
+
45
+ def seed_to_privkey_hd(seed, address_index=0):
46
+ I = hmac.new(b"Bitcoin seed", seed, hashlib.sha512).digest()
47
+ key, chain = I[:32], I[32:]
48
+ path = [44 + _HARDENED, 60 + _HARDENED, _HARDENED, 0, address_index]
49
+ for idx in path:
50
+ key, chain = _bip32_derive_child_priv(key, chain, idx)
51
+ return key
52
+
53
+
54
+ # ---------- BIP39 ----------
55
+ try:
56
+ import bip39 as _bip39_rust
57
+ _USE_RUST_BIP39 = True
58
+ except ImportError:
59
+ _bip39_rust = None
60
+ _USE_RUST_BIP39 = False
61
+
62
+ _MNEMONIC = None
63
+
64
+ def _get_mnemonic():
65
+ global _MNEMONIC
66
+ if _MNEMONIC is None:
67
+ from mnemonic import Mnemonic
68
+ _MNEMONIC = Mnemonic("english")
69
+ return _MNEMONIC
70
+
71
+ def is_valid_bip39_fast(phrase):
72
+ if _USE_RUST_BIP39:
73
+ try:
74
+ _bip39_rust.bip39_mnemonic_to_entropy(phrase)
75
+ return True
76
+ except Exception:
77
+ return False
78
+ return _get_mnemonic().check(phrase)
79
+
80
+ def phrase_to_seed(phrase):
81
+ if _USE_RUST_BIP39:
82
+ return _bip39_rust.bip39_mnemonic_to_seed(phrase, BIP39_PASSPHRASE)
83
+ return _get_mnemonic().to_seed(phrase, passphrase=BIP39_PASSPHRASE)
84
+
85
+
86
+ # ---------- Address file (lowercase) ----------
87
+ def load_addresses(path):
88
+ s = set()
89
+ with open(path, "r", encoding="utf-8") as f:
90
+ for line in f:
91
+ a = line.strip().strip('"').strip("'").lstrip("\ufeff").lower()
92
+ if a.startswith("0x"):
93
+ a = a[2:]
94
+ if a and len(a) == 40:
95
+ s.add(a)
96
+ return frozenset(s)
97
+
98
+ def normalize(addr):
99
+ a = addr.strip().lower()
100
+ if a.startswith("0x"):
101
+ a = a[2:]
102
+ return a
103
+
104
+
105
+ # ---------- Generator ----------
106
+ def generate_phrases():
107
+ n = len(words)
108
+ if PHRASE_MODE == "order":
109
+ for perm in permutations(words):
110
+ yield " ".join(perm)
111
+ elif PHRASE_MODE == "replace":
112
+ for i in range(n):
113
+ for w in words:
114
+ new = words.copy()
115
+ new[i] = w
116
+ yield " ".join(new)
117
+ elif PHRASE_MODE == "subset":
118
+ for r in range(1, n + 1):
119
+ for combo in combinations(words, r):
120
+ yield " ".join(combo)
121
+
122
+ def count_total():
123
+ n = len(words)
124
+ if PHRASE_MODE == "order": return math.factorial(n)
125
+ if PHRASE_MODE == "replace": return n * n
126
+ if PHRASE_MODE == "subset": return 2**n - 1
127
+
128
+
129
+ # ---------- Worker ----------
130
+ _KNOWN = None
131
+
132
+ def init_worker(known):
133
+ global _KNOWN
134
+ _KNOWN = known
135
+ _get_mnemonic()
136
+
137
+ def process_batch(batch):
138
+ hits = []
139
+ known = _KNOWN
140
+ m = _get_mnemonic()
141
+ for phrase in batch:
142
+ if CHECK_BIP39_CHECKSUM and not is_valid_bip39_fast(phrase):
143
+ continue
144
+ try:
145
+ seed = phrase_to_seed(phrase)
146
+ if HD_DERIVATION:
147
+ priv = seed_to_privkey_hd(seed, ADDRESS_INDEX)
148
+ else:
149
+ priv = seed[:32]
150
+ pub = coincurve.PrivateKey(priv).public_key.format(compressed=False)[1:]
151
+ addr = to_checksum_address(keccak(pub)[-20:])
152
+ except Exception:
153
+ continue
154
+ if normalize(addr) in known:
155
+ hits.append((phrase, priv.hex(), addr))
156
+ return hits
157
+
158
+ def batch_generator():
159
+ batch = []
160
+ for phrase in generate_phrases():
161
+ batch.append(phrase)
162
+ if len(batch) >= BATCH_SIZE:
163
+ yield batch
164
+ batch = []
165
+ if batch:
166
+ yield batch
167
+
168
+
169
+ # ---------- Main ----------
170
+ def main():
171
+ if not os.path.exists(ADDRESSES_FILE):
172
+ raise SystemExit(f"File nahi mili: {ADDRESSES_FILE}")
173
+
174
+ total = count_total()
175
+ print(f"\nMode: {PHRASE_MODE} | Total: {total:,} | Workers: {NUM_WORKERS}")
176
+ print(f"HD: {HD_DERIVATION} (index {ADDRESS_INDEX})\n")
177
+
178
+ known = load_addresses(ADDRESSES_FILE)
179
+ print(f"Loaded {len(known):,} lowercase addresses\n")
180
+
181
+ matches = []
182
+ done = 0
183
+ start = time.time()
184
+ last_log = start
185
+
186
+ if os.path.exists(MATCHED_FILE):
187
+ os.remove(MATCHED_FILE)
188
+
189
+ gc.collect()
190
+ gc.freeze()
191
+
192
+ with Pool(processes=NUM_WORKERS,
193
+ initializer=init_worker,
194
+ initargs=(known,),
195
+ maxtasksperchild=1000) as pool:
196
+ for hits in pool.imap_unordered(process_batch, batch_generator(), chunksize=1):
197
+ done += BATCH_SIZE
198
+ for phrase, key_hex, addr in hits:
199
+ print(f"\n✅ MATCH: {phrase}\n priv: {key_hex}\n addr: {addr}\n")
200
+ matches.append((phrase, key_hex, addr))
201
+ with open(MATCHED_FILE, "a", encoding="utf-8") as f:
202
+ f.write(f"{phrase} | {key_hex} | {addr}\n")
203
+
204
+ now = time.time()
205
+ if now - last_log >= 5:
206
+ elapsed = now - start
207
+ rate = done / elapsed if elapsed else 0
208
+ eta = (total - done) / rate if rate else 0
209
+ print(f" ...{done:,}/{total:,} ({100*done/total:.2f}%) ETA {eta/60:.1f}min")
210
+ last_log = now
211
+
212
+ elapsed = time.time() - start
213
+ print(f"\nDone {elapsed:.1f}s. {len(matches)} match(es).")
214
+
215
+
216
+ if __name__ == "__main__":
217
+ main()