DILSHAD737 commited on
Commit
9145683
Β·
1 Parent(s): 4d5d278

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +304 -32
app.py CHANGED
@@ -1,33 +1,305 @@
1
- with gr.Row():
2
- my_phrase = gr.Textbox(
3
- label="Your Phrase (optional)",
4
- value="lesson cushion crane affair federal decline limb spend reunion chief cart zebra",
5
- lines=2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  )
7
-
8
- with gr.Row():
9
- num_phrases = gr.Number(label="Random Phrases to Check", value=10, minimum=0)
10
- addresses_file = gr.Textbox(label="Addresses File", value="12730802_addresses.csv")
11
-
12
- with gr.Row():
13
- phrase_mode = gr.Radio(choices=["replace", "order", "subset"], value="replace", label="Phrase Mode")
14
- start_index = gr.Number(label="Start Index", value=0, minimum=0)
15
- end_index = gr.Number(label="End Index", value=20, minimum=0)
16
-
17
- with gr.Row():
18
- passphrase = gr.Textbox(label="BIP39 Passphrase", value="", type="password")
19
-
20
- run_btn = gr.Button("πŸš€ Start Scan", variant="primary")
21
-
22
- gr.Markdown("### Output")
23
- log_output = gr.Textbox(lines=15, interactive=False, show_label=False)
24
-
25
- gr.Markdown("### Matches")
26
- matches_output = gr.Textbox(lines=8, interactive=False, show_label=False)
27
-
28
- run_btn.click(
29
- fn=run_scan,
30
- inputs=[my_phrase, num_phrases, addresses_file, phrase_mode,
31
- start_index, end_index, passphrase],
32
- outputs=[log_output, matches_output]
33
- )
 
1
+ import asyncio
2
+ import warnings
3
+ warnings.filterwarnings("ignore", category=UserWarning)
4
+ warnings.filterwarnings("ignore", category=RuntimeWarning)
5
+
6
+ try:
7
+ asyncio.set_event_loop(asyncio.new_event_loop())
8
+ except Exception:
9
+ pass
10
+
11
+ import gradio as gr
12
+ import hashlib, hmac, os, time, math, gc, random
13
+ from itertools import permutations, combinations
14
+ from multiprocessing import Pool
15
+ from mnemonic import Mnemonic
16
+ import coincurve
17
+ from eth_utils import keccak, to_checksum_address
18
+ import spaces
19
+
20
+ # ============================================================
21
+ # πŸ”‘ APNI PHRASE YAHAN DALEIN
22
+ MY_PHRASE = "lesson cushion crane affair federal decline limb spend reunion chief cart zebra"
23
+ # ============================================================
24
+
25
+ @spaces.GPU
26
+ def _noop():
27
+ return "ok"
28
+
29
+ # ---------- Mnemonic ----------
30
+ _MNEMONIC = None
31
+ def _get_mnemonic():
32
+ global _MNEMONIC
33
+ if _MNEMONIC is None:
34
+ _MNEMONIC = Mnemonic("english")
35
+ return _MNEMONIC
36
+
37
+ # ---------- BIP32 manual ----------
38
+ _SECP256K1_N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
39
+ _HARDENED = 0x80000000
40
+
41
+ def _bip32_derive_child_priv(parent_priv, parent_chain, index):
42
+ if index >= _HARDENED:
43
+ data = b'\x00' + parent_priv + index.to_bytes(4, 'big')
44
+ else:
45
+ pub = coincurve.PrivateKey(parent_priv).public_key.format(compressed=True)
46
+ data = pub + index.to_bytes(4, 'big')
47
+ I = hmac.new(parent_chain, data, hashlib.sha512).digest()
48
+ child_int = (int.from_bytes(I[:32], 'big') + int.from_bytes(parent_priv, 'big')) % _SECP256K1_N
49
+ if child_int == 0:
50
+ raise ValueError("Invalid child key")
51
+ return child_int.to_bytes(32, 'big'), I[32:]
52
+
53
+ def seed_to_privkey_hd(seed, address_index=0):
54
+ I = hmac.new(b"Bitcoin seed", seed, hashlib.sha512).digest()
55
+ key, chain = I[:32], I[32:]
56
+ for idx in [44 + _HARDENED, 60 + _HARDENED, _HARDENED, 0, address_index]:
57
+ key, chain = _bip32_derive_child_priv(key, chain, idx)
58
+ return key
59
+
60
+ # ---------- BIP39 ----------
61
+ try:
62
+ import bip39 as _bip39_rust
63
+ _USE_RUST_BIP39 = True
64
+ if hasattr(_bip39_rust, 'phrase_to_seed'):
65
+ _seed_fn = _bip39_rust.phrase_to_seed
66
+ _check_fn = getattr(_bip39_rust, 'check_phrase', None)
67
+ elif hasattr(_bip39_rust, 'bip39_to_seed'):
68
+ _seed_fn = _bip39_rust.bip39_to_seed
69
+ _check_fn = getattr(_bip39_rust, 'bip39_validate', None)
70
+ else:
71
+ _seed_fn = None; _check_fn = None; _USE_RUST_BIP39 = False
72
+ except ImportError:
73
+ _bip39_rust = None; _USE_RUST_BIP39 = False; _seed_fn = None; _check_fn = None
74
+
75
+ def is_valid_bip39(phrase):
76
+ if _USE_RUST_BIP39 and _check_fn:
77
+ return _check_fn(phrase)
78
+ return _get_mnemonic().check(phrase)
79
+
80
+ def phrase_to_seed(phrase, passphrase=""):
81
+ if _USE_RUST_BIP39 and _seed_fn:
82
+ return _seed_fn(phrase, passphrase)
83
+ return _get_mnemonic().to_seed(phrase, passphrase=passphrase)
84
+
85
+ # ---------- Address file ----------
86
+ def load_addresses(path):
87
+ s = set()
88
+ with open(path, "r", encoding="utf-8") as f:
89
+ for line in f:
90
+ a = line.strip().strip('"').strip("'").lstrip("\ufeff").lower()
91
+ if a.startswith("0x"): a = a[2:]
92
+ if a and len(a) == 40: s.add(a)
93
+ return frozenset(s)
94
+
95
+ def normalize(addr):
96
+ a = addr.strip().lower()
97
+ if a.startswith("0x"): a = a[2:]
98
+ return a
99
+
100
+ # ---------- Generator ----------
101
+ def generate_phrases_for_words(wordlist, mode):
102
+ n = len(wordlist)
103
+ if mode == "order":
104
+ for perm in permutations(wordlist):
105
+ yield " ".join(perm)
106
+ elif mode == "replace":
107
+ for i in range(n):
108
+ for w in wordlist:
109
+ new = wordlist.copy()
110
+ new[i] = w
111
+ yield " ".join(new)
112
+ elif mode == "subset":
113
+ for r in range(1, n + 1):
114
+ for combo in combinations(wordlist, r):
115
+ yield " ".join(combo)
116
+
117
+ # ---------- Worker ----------
118
+ _KNOWN = None
119
+
120
+ def init_worker(known):
121
+ global _KNOWN
122
+ _KNOWN = known
123
+ _get_mnemonic()
124
+
125
+ def process_batch(args):
126
+ batch, passphrase, address_index, hd, checksum = args
127
+ hits = []
128
+ known = _KNOWN
129
+ for phrase in batch:
130
+ if checksum and not is_valid_bip39(phrase):
131
+ continue
132
+ try:
133
+ seed = phrase_to_seed(phrase, passphrase)
134
+ priv = seed_to_privkey_hd(seed, address_index) if hd else seed[:32]
135
+ pub = coincurve.PrivateKey(priv).public_key.format(compressed=False)[1:]
136
+ addr = to_checksum_address(keccak(pub)[-20:])
137
+ except Exception:
138
+ continue
139
+ if normalize(addr) in known:
140
+ hits.append((phrase, priv.hex(), addr))
141
+ return hits
142
+
143
+ def batch_generator_for_words(wordlist, mode, batch_size):
144
+ batch = []
145
+ for phrase in generate_phrases_for_words(wordlist, mode):
146
+ batch.append(phrase)
147
+ if len(batch) >= batch_size:
148
+ yield batch
149
+ batch = []
150
+ if batch:
151
+ yield batch
152
+
153
+ # ============================================================
154
+ # Gradio Interface
155
+ # ============================================================
156
+ def run_scan(
157
+ my_phrase,
158
+ addresses_file,
159
+ num_phrases,
160
+ phrase_mode,
161
+ address_index,
162
+ passphrase,
163
+ workers,
164
+ batch_size,
165
+ progress=gr.Progress(),
166
+ ):
167
+ if not os.path.exists(addresses_file):
168
+ return f"❌ File nahi mili: {addresses_file}", ""
169
+
170
+ num_phrases = int(num_phrases)
171
+ workers = int(workers)
172
+ batch_size = int(batch_size)
173
+ address_index = int(address_index)
174
+
175
+ if num_phrases < 0:
176
+ return "❌ Phrases ki tadaad negative nahi ho sakti", ""
177
+ if workers < 1:
178
+ return "❌ Kam az kam 1 worker chahiye", ""
179
+
180
+ yield "Addresses load ho rahe hain...", ""
181
+ try:
182
+ known = load_addresses(addresses_file)
183
+ except Exception as e:
184
+ return f"❌ Addresses load fail: {e}", ""
185
+
186
+ if not known:
187
+ return "❌ Koi valid address file mein nahi mila", ""
188
+
189
+ wordlist = _get_mnemonic().wordlist
190
+ random_phrases = [random.sample(wordlist, 12) for _ in range(num_phrases)]
191
+
192
+ # πŸ”‘ Apni phrase sab se pehle
193
+ my_words = my_phrase.strip().split()
194
+ if len(my_words) == 12:
195
+ random_phrases.insert(0, my_words)
196
+ phrase_note = "βœ… Aapki phrase sab se pehle check hogi"
197
+ else:
198
+ phrase_note = f"⚠️ Aapki phrase 12 words ki nahi hai ({len(my_words)} words) β€” skip"
199
+
200
+ total_phrases = len(random_phrases)
201
+ log_lines = [
202
+ phrase_note,
203
+ f"βœ… Loaded {len(known):,} addresses",
204
+ f"βœ… {num_phrases} random phrases + 1 aapki phrase = {total_phrases} total",
205
+ f"πŸ”§ Mode: {phrase_mode} | Workers: {workers} | Batch: {batch_size}",
206
+ f"πŸ”‘ Address index: {address_index} | Passphrase: {'(set)' if passphrase else '(empty)'}",
207
+ "",
208
+ ]
209
+
210
+ output_matches = []
211
+ matches_text = ""
212
+ all_start = time.time()
213
+
214
+ for idx, wordlist_words in enumerate(random_phrases, 1):
215
+ progress((idx - 1) / total_phrases, desc=f"Phrase {idx}/{total_phrases}")
216
+
217
+ gc.collect()
218
+ gc.freeze()
219
+
220
+ phrase_matches = []
221
+
222
+ with Pool(processes=workers,
223
+ initializer=init_worker,
224
+ initargs=(known,)) as pool:
225
+ for hits in pool.imap_unordered(
226
+ process_batch,
227
+ (
228
+ (batch, passphrase, address_index, True, True)
229
+ for batch in batch_generator_for_words(wordlist_words, phrase_mode, batch_size)
230
+ ),
231
+ chunksize=4
232
+ ):
233
+ for phrase, key_hex, addr in hits:
234
+ match_line = f"πŸŽ‰ MATCH!\nPhrase: {phrase}\nKey: {key_hex}\nAddr: {addr}\n"
235
+ matches_text += match_line + "\n"
236
+ output_matches.append((phrase, key_hex, addr))
237
+ phrase_matches.append((phrase, key_hex, addr))
238
+ log_lines.append(match_line)
239
+ yield "\n".join(log_lines), matches_text
240
+
241
+ log_lines.append(f"Phrase {idx}/{total_phrases} done β€” {len(phrase_matches)} match(es)")
242
+ yield "\n".join(log_lines), matches_text
243
+
244
+ elapsed = time.time() - all_start
245
+ progress(1.0, desc="Done")
246
+ log_lines.append("")
247
+ log_lines.append(f"=== DONE ===")
248
+ log_lines.append(f"Total time: {elapsed:.1f}s")
249
+ log_lines.append(f"Total matches: {len(output_matches)}")
250
+
251
+ yield "\n".join(log_lines), matches_text
252
+
253
+
254
+ # ============================================================
255
+ # Build UI
256
+ # ============================================================
257
+ with gr.Blocks(title="Phrase Scanner") as demo:
258
+ gr.Markdown("# πŸ” Phrase Scanner")
259
+ gr.Markdown("Aapki phrase + random 12-word phrases generate karein aur address file se match karein.")
260
+
261
+ with gr.Row():
262
+ with gr.Column(scale=1):
263
+ gr.Markdown("### Settings")
264
+ my_phrase = gr.Textbox(
265
+ label="πŸ”‘ Aapki Phrase (12 words)",
266
+ value=MY_PHRASE,
267
+ placeholder="lesson cushion crane ...",
268
+ lines=2,
269
+ )
270
+ addresses_file = gr.Textbox(label="Addresses File", value="12730802_addresses.csv")
271
+ num_phrases = gr.Slider(label="Kitni Random Phrases", minimum=0, maximum=1000, value=10, step=1)
272
+ phrase_mode = gr.Radio(choices=["replace", "order", "subset"], value="replace", label="Phrase Mode")
273
+ address_index = gr.Number(label="Address Index", value=0, precision=0)
274
+ passphrase = gr.Textbox(label="BIP39 Passphrase (khali chhod dein)", value="", type="password")
275
+ workers = gr.Slider(label="Workers", minimum=1, maximum=192, value=48, step=1)
276
+ batch_size = gr.Slider(label="Batch Size", minimum=100, maximum=10000, value=5000, step=100)
277
+ run_btn = gr.Button("πŸš€ Scan Shuru Karein", variant="primary")
278
+
279
+ with gr.Column(scale=2):
280
+ gr.Markdown("### Output")
281
+ log_output = gr.Textbox(label="Scan Log", lines=20, max_lines=30, interactive=False, autoscroll=True)
282
+ matches_output = gr.Textbox(label="Matches", lines=10, interactive=False, autoscroll=True)
283
+
284
+ run_btn.click(
285
+ fn=run_scan,
286
+ inputs=[my_phrase, addresses_file, num_phrases, phrase_mode,
287
+ address_index, passphrase, workers, batch_size],
288
+ outputs=[log_output, matches_output],
289
  )
290
+
291
+ # ZeroGPU requirement
292
+ _noop()
293
+
294
+ # βœ… SSR off + share for health check
295
+ _original_launch = demo.launch
296
+ def _patched_launch(**kwargs):
297
+ kwargs.setdefault("ssr_mode", False)
298
+ kwargs.setdefault("share", True)
299
+ kwargs.setdefault("server_name", "0.0.0.0")
300
+ kwargs.setdefault("server_port", 7860)
301
+ return _original_launch(**kwargs)
302
+ demo.launch = _patched_launch
303
+
304
+ if __name__ == "__main__":
305
+ demo.launch(theme=gr.themes.Soft())