|
12 | 12 | using System.Diagnostics; |
13 | 13 | using System.Linq; |
14 | 14 | using System.Numerics; |
| 15 | +using System.Threading; |
15 | 16 | using System.Threading.Tasks; |
16 | 17 |
|
17 | 18 | namespace FinderOuter.Services |
@@ -334,6 +335,258 @@ private unsafe bool LoopUncomp() |
334 | 335 | return success; |
335 | 336 | } |
336 | 337 |
|
| 338 | + private unsafe bool SpecialLoopComp(string key) |
| 339 | + { |
| 340 | + byte[] padded; |
| 341 | + int uLen; |
| 342 | + |
| 343 | + // Maximum result (58^52) is 39 bytes = 39/4 = 10 uint |
| 344 | + uLen = 10; |
| 345 | + uint[] powers58 = new uint[Constants.CompPrivKeyLen * uLen]; |
| 346 | + padded = new byte[4 * uLen]; |
| 347 | + |
| 348 | + for (int i = 0, j = 0; i < Constants.CompPrivKeyLen; i++) |
| 349 | + { |
| 350 | + BigInteger val = BigInteger.Pow(58, i); |
| 351 | + byte[] temp = val.ToByteArray(true, false); |
| 352 | + |
| 353 | + Array.Clear(padded, 0, padded.Length); |
| 354 | + Buffer.BlockCopy(temp, 0, padded, 0, temp.Length); |
| 355 | + |
| 356 | + for (int k = 0; k < padded.Length; j++, k += 4) |
| 357 | + { |
| 358 | + powers58[j] = (uint)(padded[k] << 0 | padded[k + 1] << 8 | padded[k + 2] << 16 | padded[k + 3] << 24); |
| 359 | + } |
| 360 | + } |
| 361 | + |
| 362 | + int[] values = new int[key.Length]; |
| 363 | + for (int i = 0; i < values.Length; i++) |
| 364 | + { |
| 365 | + values[i] = Constants.Base58Chars.IndexOf(key[i]); |
| 366 | + } |
| 367 | + |
| 368 | + uint[] precomputed = new uint[uLen]; |
| 369 | + |
| 370 | + // i starts from 1 becaue it is compressed (K or L) |
| 371 | + for (int i = 1; i < Constants.CompPrivKeyLen - 2; i++) |
| 372 | + { |
| 373 | + for (int j = i + 1; j < Constants.CompPrivKeyLen - 1; j++) |
| 374 | + { |
| 375 | + for (int k = j + 1; k < Constants.CompPrivKeyLen; k++) |
| 376 | + { |
| 377 | + Array.Clear(precomputed, 0, precomputed.Length); |
| 378 | + |
| 379 | + for (int index = 0; index < i; index++) |
| 380 | + { |
| 381 | + ulong carry = 0; |
| 382 | + ulong val = (ulong)values[index]; |
| 383 | + int powIndex = (Constants.CompPrivKeyLen - 1 - index) * uLen; |
| 384 | + for (int m = uLen - 1; m >= 0; m--, powIndex++) |
| 385 | + { |
| 386 | + ulong result = checked((powers58[powIndex] * val) + precomputed[m] + carry); |
| 387 | + precomputed[m] = (uint)result; |
| 388 | + carry = (uint)(result >> 32); |
| 389 | + } |
| 390 | + } |
| 391 | + |
| 392 | + for (int index = i + 1; index < j; index++) |
| 393 | + { |
| 394 | + ulong carry = 0; |
| 395 | + ulong val = (ulong)values[index - 1]; |
| 396 | + int powIndex = (Constants.CompPrivKeyLen - 1 - index) * uLen; |
| 397 | + for (int m = uLen - 1; m >= 0; m--, powIndex++) |
| 398 | + { |
| 399 | + ulong result = checked((powers58[powIndex] * val) + precomputed[m] + carry); |
| 400 | + precomputed[m] = (uint)result; |
| 401 | + carry = (uint)(result >> 32); |
| 402 | + } |
| 403 | + } |
| 404 | + |
| 405 | + for (int index = j + 1; index < k; index++) |
| 406 | + { |
| 407 | + ulong carry = 0; |
| 408 | + ulong val = (ulong)values[index - 2]; |
| 409 | + int powIndex = (Constants.CompPrivKeyLen - 1 - index) * uLen; |
| 410 | + for (int m = uLen - 1; m >= 0; m--, powIndex++) |
| 411 | + { |
| 412 | + ulong result = checked((powers58[powIndex] * val) + precomputed[m] + carry); |
| 413 | + precomputed[m] = (uint)result; |
| 414 | + carry = (uint)(result >> 32); |
| 415 | + } |
| 416 | + } |
| 417 | + |
| 418 | + for (int index = k + 1; index < Constants.CompPrivKeyLen; index++) |
| 419 | + { |
| 420 | + ulong carry = 0; |
| 421 | + ulong val = (ulong)values[index - 3]; |
| 422 | + int powIndex = (Constants.CompPrivKeyLen - 1 - index) * uLen; |
| 423 | + for (int m = uLen - 1; m >= 0; m--, powIndex++) |
| 424 | + { |
| 425 | + ulong result = checked((powers58[powIndex] * val) + precomputed[m] + carry); |
| 426 | + precomputed[m] = (uint)result; |
| 427 | + carry = (uint)(result >> 32); |
| 428 | + } |
| 429 | + } |
| 430 | + |
| 431 | + var cancelToken = new CancellationTokenSource(); |
| 432 | + var options = new ParallelOptions |
| 433 | + { |
| 434 | + CancellationToken = cancelToken.Token, |
| 435 | + MaxDegreeOfParallelism = 4 |
| 436 | + }; |
| 437 | + |
| 438 | + try |
| 439 | + { |
| 440 | + Parallel.For(0, 58, options, (c1, loopState) => |
| 441 | + { |
| 442 | + for (int c2 = 0; c2 < 58; c2++) |
| 443 | + { |
| 444 | + for (int c3 = 0; c3 < 58; c3++) |
| 445 | + { |
| 446 | + options.CancellationToken.ThrowIfCancellationRequested(); |
| 447 | + |
| 448 | + uint[] temp = new uint[precomputed.Length]; |
| 449 | + Array.Copy(precomputed, temp, precomputed.Length); |
| 450 | + |
| 451 | + ulong carry = 0; |
| 452 | + ulong val = (ulong)c1; |
| 453 | + int powIndex = (Constants.CompPrivKeyLen - 1 - i) * uLen; |
| 454 | + for (int m = uLen - 1; m >= 0; m--, powIndex++) |
| 455 | + { |
| 456 | + ulong result = checked((powers58[powIndex] * val) + temp[m] + carry); |
| 457 | + temp[m] = (uint)result; |
| 458 | + carry = (uint)(result >> 32); |
| 459 | + } |
| 460 | + |
| 461 | + carry = 0; |
| 462 | + val = (ulong)c2; |
| 463 | + powIndex = (Constants.CompPrivKeyLen - 1 - j) * uLen; |
| 464 | + for (int m = uLen - 1; m >= 0; m--, powIndex++) |
| 465 | + { |
| 466 | + ulong result = checked((powers58[powIndex] * val) + temp[m] + carry); |
| 467 | + temp[m] = (uint)result; |
| 468 | + carry = (uint)(result >> 32); |
| 469 | + } |
| 470 | + |
| 471 | + carry = 0; |
| 472 | + val = (ulong)c3; |
| 473 | + powIndex = (Constants.CompPrivKeyLen - 1 - k) * uLen; |
| 474 | + for (int m = uLen - 1; m >= 0; m--, powIndex++) |
| 475 | + { |
| 476 | + ulong result = checked((powers58[powIndex] * val) + temp[m] + carry); |
| 477 | + temp[m] = (uint)result; |
| 478 | + carry = (uint)(result >> 32); |
| 479 | + } |
| 480 | + |
| 481 | + if (Compute(temp)) |
| 482 | + { |
| 483 | + string foundRes = key.Insert(i, $"{Constants.Base58Chars[c1]}") |
| 484 | + .Insert(j, $"{Constants.Base58Chars[c2]}") |
| 485 | + .Insert(k, $"{Constants.Base58Chars[c3]}"); |
| 486 | + AddQueue($"Found a key: {foundRes}"); |
| 487 | + Task.Run(() => cancelToken.Cancel()); |
| 488 | + } |
| 489 | + } |
| 490 | + } |
| 491 | + }); |
| 492 | + } |
| 493 | + catch (Exception) |
| 494 | + { |
| 495 | + return true; |
| 496 | + } |
| 497 | + } |
| 498 | + } |
| 499 | + } |
| 500 | + |
| 501 | + return false; |
| 502 | + } |
| 503 | + |
| 504 | + |
| 505 | + private unsafe bool Compute(uint[] keyValueInts) |
| 506 | + { |
| 507 | + // Sha must be defined here for this method to be thread safe |
| 508 | + Sha256 sha = new Sha256(); |
| 509 | + |
| 510 | + fixed (uint* hPt = &sha.hashState[0], wPt = &sha.w[0]) |
| 511 | + fixed (uint* keyPt = &keyValueInts[0]) |
| 512 | + { |
| 513 | + wPt[0] = (keyPt[0] << 16) | (keyPt[1] >> 16); |
| 514 | + wPt[1] = (keyPt[1] << 16) | (keyPt[2] >> 16); |
| 515 | + wPt[2] = (keyPt[2] << 16) | (keyPt[3] >> 16); |
| 516 | + wPt[3] = (keyPt[3] << 16) | (keyPt[4] >> 16); |
| 517 | + wPt[4] = (keyPt[4] << 16) | (keyPt[5] >> 16); |
| 518 | + wPt[5] = (keyPt[5] << 16) | (keyPt[6] >> 16); |
| 519 | + wPt[6] = (keyPt[6] << 16) | (keyPt[7] >> 16); |
| 520 | + wPt[7] = (keyPt[7] << 16) | (keyPt[8] >> 16); |
| 521 | + wPt[8] = (keyPt[8] << 16) | 0b00000000_00000000_10000000_00000000U; |
| 522 | + // from 9 to 14 =0 |
| 523 | + wPt[15] = 272; // 34 *8 = 272 |
| 524 | + |
| 525 | + //for (int i = 16; i < w.Length; i++) |
| 526 | + //{ |
| 527 | + // wPt[i] = SSIG1(wPt[i - 2]) + wPt[i - 7] + SSIG0(wPt[i - 15]) + wPt[i - 16]; |
| 528 | + //} |
| 529 | + |
| 530 | + wPt[16] = sha.SSIG0(wPt[1]) + wPt[0]; |
| 531 | + wPt[17] = 11141120 + sha.SSIG0(wPt[2]) + wPt[1]; |
| 532 | + wPt[18] = sha.SSIG1(wPt[16]) + sha.SSIG0(wPt[3]) + wPt[2]; |
| 533 | + wPt[19] = sha.SSIG1(wPt[17]) + sha.SSIG0(wPt[4]) + wPt[3]; |
| 534 | + wPt[20] = sha.SSIG1(wPt[18]) + sha.SSIG0(wPt[5]) + wPt[4]; |
| 535 | + wPt[21] = sha.SSIG1(wPt[19]) + sha.SSIG0(wPt[6]) + wPt[5]; |
| 536 | + wPt[22] = sha.SSIG1(wPt[20]) + 272 + sha.SSIG0(wPt[7]) + wPt[6]; |
| 537 | + wPt[23] = sha.SSIG1(wPt[21]) + wPt[16] + sha.SSIG0(wPt[8]) + wPt[7]; |
| 538 | + wPt[24] = sha.SSIG1(wPt[22]) + wPt[17] + wPt[8]; |
| 539 | + wPt[25] = sha.SSIG1(wPt[23]) + wPt[18]; |
| 540 | + wPt[26] = sha.SSIG1(wPt[24]) + wPt[19]; |
| 541 | + wPt[27] = sha.SSIG1(wPt[25]) + wPt[20]; |
| 542 | + wPt[28] = sha.SSIG1(wPt[26]) + wPt[21]; |
| 543 | + wPt[29] = sha.SSIG1(wPt[27]) + wPt[22]; |
| 544 | + wPt[30] = sha.SSIG1(wPt[28]) + wPt[23] + 541327392; |
| 545 | + wPt[31] = sha.SSIG1(wPt[29]) + wPt[24] + sha.SSIG0(wPt[16]) + 272; |
| 546 | + wPt[32] = sha.SSIG1(wPt[30]) + wPt[25] + sha.SSIG0(wPt[17]) + wPt[16]; |
| 547 | + wPt[33] = sha.SSIG1(wPt[31]) + wPt[26] + sha.SSIG0(wPt[18]) + wPt[17]; |
| 548 | + wPt[34] = sha.SSIG1(wPt[32]) + wPt[27] + sha.SSIG0(wPt[19]) + wPt[18]; |
| 549 | + wPt[35] = sha.SSIG1(wPt[33]) + wPt[28] + sha.SSIG0(wPt[20]) + wPt[19]; |
| 550 | + wPt[36] = sha.SSIG1(wPt[34]) + wPt[29] + sha.SSIG0(wPt[21]) + wPt[20]; |
| 551 | + wPt[37] = sha.SSIG1(wPt[35]) + wPt[30] + sha.SSIG0(wPt[22]) + wPt[21]; |
| 552 | + wPt[38] = sha.SSIG1(wPt[36]) + wPt[31] + sha.SSIG0(wPt[23]) + wPt[22]; |
| 553 | + wPt[39] = sha.SSIG1(wPt[37]) + wPt[32] + sha.SSIG0(wPt[24]) + wPt[23]; |
| 554 | + wPt[40] = sha.SSIG1(wPt[38]) + wPt[33] + sha.SSIG0(wPt[25]) + wPt[24]; |
| 555 | + wPt[41] = sha.SSIG1(wPt[39]) + wPt[34] + sha.SSIG0(wPt[26]) + wPt[25]; |
| 556 | + wPt[42] = sha.SSIG1(wPt[40]) + wPt[35] + sha.SSIG0(wPt[27]) + wPt[26]; |
| 557 | + wPt[43] = sha.SSIG1(wPt[41]) + wPt[36] + sha.SSIG0(wPt[28]) + wPt[27]; |
| 558 | + wPt[44] = sha.SSIG1(wPt[42]) + wPt[37] + sha.SSIG0(wPt[29]) + wPt[28]; |
| 559 | + wPt[45] = sha.SSIG1(wPt[43]) + wPt[38] + sha.SSIG0(wPt[30]) + wPt[29]; |
| 560 | + wPt[46] = sha.SSIG1(wPt[44]) + wPt[39] + sha.SSIG0(wPt[31]) + wPt[30]; |
| 561 | + wPt[47] = sha.SSIG1(wPt[45]) + wPt[40] + sha.SSIG0(wPt[32]) + wPt[31]; |
| 562 | + wPt[48] = sha.SSIG1(wPt[46]) + wPt[41] + sha.SSIG0(wPt[33]) + wPt[32]; |
| 563 | + wPt[49] = sha.SSIG1(wPt[47]) + wPt[42] + sha.SSIG0(wPt[34]) + wPt[33]; |
| 564 | + wPt[50] = sha.SSIG1(wPt[48]) + wPt[43] + sha.SSIG0(wPt[35]) + wPt[34]; |
| 565 | + wPt[51] = sha.SSIG1(wPt[49]) + wPt[44] + sha.SSIG0(wPt[36]) + wPt[35]; |
| 566 | + wPt[52] = sha.SSIG1(wPt[50]) + wPt[45] + sha.SSIG0(wPt[37]) + wPt[36]; |
| 567 | + wPt[53] = sha.SSIG1(wPt[51]) + wPt[46] + sha.SSIG0(wPt[38]) + wPt[37]; |
| 568 | + wPt[54] = sha.SSIG1(wPt[52]) + wPt[47] + sha.SSIG0(wPt[39]) + wPt[38]; |
| 569 | + wPt[55] = sha.SSIG1(wPt[53]) + wPt[48] + sha.SSIG0(wPt[40]) + wPt[39]; |
| 570 | + wPt[56] = sha.SSIG1(wPt[54]) + wPt[49] + sha.SSIG0(wPt[41]) + wPt[40]; |
| 571 | + wPt[57] = sha.SSIG1(wPt[55]) + wPt[50] + sha.SSIG0(wPt[42]) + wPt[41]; |
| 572 | + wPt[58] = sha.SSIG1(wPt[56]) + wPt[51] + sha.SSIG0(wPt[43]) + wPt[42]; |
| 573 | + wPt[59] = sha.SSIG1(wPt[57]) + wPt[52] + sha.SSIG0(wPt[44]) + wPt[43]; |
| 574 | + wPt[60] = sha.SSIG1(wPt[58]) + wPt[53] + sha.SSIG0(wPt[45]) + wPt[44]; |
| 575 | + wPt[61] = sha.SSIG1(wPt[59]) + wPt[54] + sha.SSIG0(wPt[46]) + wPt[45]; |
| 576 | + wPt[62] = sha.SSIG1(wPt[60]) + wPt[55] + sha.SSIG0(wPt[47]) + wPt[46]; |
| 577 | + wPt[63] = sha.SSIG1(wPt[61]) + wPt[56] + sha.SSIG0(wPt[48]) + wPt[47]; |
| 578 | + |
| 579 | + sha.Init(hPt); |
| 580 | + sha.CompressBlockWithWSet(hPt, wPt); |
| 581 | + |
| 582 | + // Perform second hash |
| 583 | + sha.DoSecondHash(hPt, wPt); |
| 584 | + |
| 585 | + return hPt[0] == keyPt[9]; |
| 586 | + } |
| 587 | + } |
| 588 | + |
| 589 | + |
337 | 590 |
|
338 | 591 | public async Task<bool> Find(string key, char missingChar) |
339 | 592 | { |
@@ -435,5 +688,37 @@ await Task.Run(() => |
435 | 688 | return CopyQueueToMessage(success); |
436 | 689 | } |
437 | 690 |
|
| 691 | + |
| 692 | + public async Task<bool> FindUnknownLocation(string key) |
| 693 | + { |
| 694 | + InitReport(); |
| 695 | + |
| 696 | + if (string.IsNullOrEmpty(key) || key.Any(c => !Constants.Base58Chars.Contains(c))) |
| 697 | + return Fail("Input contains invalid base-58 character(s)."); |
| 698 | + if (key.Length != Constants.CompPrivKeyLen - 3 || |
| 699 | + (!key.StartsWith(Constants.CompPrivKeyChar2) && key.StartsWith(Constants.CompPrivKeyChar2))) |
| 700 | + { |
| 701 | + // Only this special case is supported for now |
| 702 | + return Fail("This option is only defined for compressed private keys that " + |
| 703 | + "have 3 missing characters with unknown locations."); |
| 704 | + } |
| 705 | + |
| 706 | + // 51! / 3! *((51-3)!) |
| 707 | + BigInteger total = ((51 * 50 * 49) / (3 * 2 * 1)) * BigInteger.Pow(58, 3); |
| 708 | + AddMessage($"Start searching.{Environment.NewLine}Total number of keys to check: {total:n0}"); |
| 709 | + |
| 710 | + Stopwatch watch = Stopwatch.StartNew(); |
| 711 | + bool success = await Task.Run(() => |
| 712 | + { |
| 713 | + return SpecialLoopComp(key); |
| 714 | + } |
| 715 | + ); |
| 716 | + |
| 717 | + watch.Stop(); |
| 718 | + AddQueue($"Elapsed time: {watch.Elapsed}"); |
| 719 | + AddQueue(GetKeyPerSec(total, watch.Elapsed.TotalSeconds)); |
| 720 | + |
| 721 | + return CopyQueueToMessage(success); |
| 722 | + } |
438 | 723 | } |
439 | 724 | } |
0 commit comments