├── .vscode ├── extensions.json └── settings.json ├── .gitignore ├── .clang-format ├── .github └── workflows │ └── ci.yml ├── LICENSE ├── to_hash.py ├── _check.py ├── Makefile ├── lib ├── addr.c ├── compat.c ├── rmd160.c ├── bench.c ├── rmd160s.c ├── utils.c ├── sha256.c └── ecc.c ├── data ├── btc-puzzles-addr ├── btc-puzzles-hash └── btc-bw-hash ├── readme.md └── main.c /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["ms-vscode.cpptools", "xaver.clang-format"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | misc/ 3 | misc-*/ 4 | *.out 5 | ecloop 6 | ecloop.exe 7 | _*.txt 8 | found_*.txt 9 | *.blf 10 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | ColumnLimit: 100 3 | AllowShortIfStatementsOnASingleLine: AllIfsAndElse 4 | IndentPPDirectives: BeforeHash 5 | AllowShortBlocksOnASingleLine: Empty 6 | AllowShortLoopsOnASingleLine: true 7 | InsertNewlineAtEOF: true 8 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[c]": { 3 | "editor.defaultFormatter": "xaver.clang-format" 4 | }, 5 | "[json]": { 6 | "editor.defaultFormatter": "esbenp.prettier-vscode" 7 | }, 8 | "[py]": { 9 | "editor.defaultFormatter": "charliermarsh.ruff" 10 | }, 11 | "editor.formatOnSave": true, 12 | "files.exclude": { 13 | "a.out": true, 14 | "ecloop": true 15 | }, 16 | "prettier.requireConfig": false, 17 | "ruff.lineLength": 99, 18 | "C_Cpp.dimInactiveRegions": false 19 | } 20 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | branches: 6 | - '**' 7 | paths: 8 | - .github/workflows/*.yml 9 | - lib/** 10 | - main.c 11 | 12 | jobs: 13 | build: 14 | strategy: 15 | matrix: 16 | os: [ubuntu-latest, macos-latest] 17 | runs-on: ${{ matrix.os }} 18 | steps: 19 | - uses: actions/checkout@v4 20 | 21 | - if: matrix.os == 'ubuntu-latest' 22 | run: sudo apt update && sudo apt install -y clang 23 | 24 | - run: clang -v 25 | - run: make build 26 | - run: ./ecloop -v 27 | - run: ./ecloop add -f data/btc-puzzles-hash -r 8000:ffff -q -o /dev/null 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 vladkens 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /to_hash.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import sys 3 | 4 | import base58 5 | import bech32 6 | 7 | 8 | def hex_encode(bytes): 9 | return "".join(f"{b:02x}" for b in bytes) 10 | 11 | 12 | def process_line(line: str): 13 | if line.startswith("s-") or line.startswith("d-") or line.startswith("m-"): 14 | return 15 | 16 | if line.startswith("bc1"): 17 | data = bech32.decode("bc", line)[1] 18 | if data is None or len(data) != 20: 19 | return "bc1" 20 | 21 | print(hex_encode(data)) 22 | 23 | if line.startswith("1") or line.startswith("3"): 24 | data = base58.b58decode(line) 25 | data = data[1 : 20 + 1] 26 | if len(data) != 20: 27 | return 28 | 29 | print(hex_encode(data)) 30 | 31 | 32 | def main(): 33 | wal_pos = int(sys.argv[1] if len(sys.argv) > 1 else "0") 34 | while True: 35 | try: 36 | line = input() 37 | line = line.strip().replace(",", ";").replace("\t", ";").replace(" ", ";") 38 | line = line.split(";")[wal_pos] 39 | process_line(line) 40 | except EOFError: 41 | break 42 | 43 | 44 | if __name__ == "__main__": 45 | main() 46 | -------------------------------------------------------------------------------- /_check.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import time 3 | from subprocess import PIPE 4 | 5 | 6 | def run(cmd: str | list[str]): 7 | stime = time.time() 8 | cmd = " ".join(cmd) if isinstance(cmd, list) else cmd 9 | res = subprocess.run(f"sh -c '{cmd}'", shell=True, text=True, stdout=PIPE, stderr=PIPE) 10 | assert res.returncode == 0, f"Failed to run command: {cmd}" 11 | return res.stdout + res.stderr, time.time() - stime 12 | 13 | 14 | def parse_final(out: str): 15 | out = [x for x in out.split("\n") if "Mkeys/s ~" in x][-1] 16 | return out.split(" ~ ")[-1].replace(",", "").strip() 17 | 18 | 19 | def check_add(hosts: list[str]): 20 | out, elapsed = run(["make", "mul"]) 21 | ref_out = parse_final(out) 22 | ref_addr33 = set([x.strip() for x in out.split("\n") if x.startswith("addr33:")]) 23 | ref_addr65 = set([x.strip() for x in out.split("\n") if x.startswith("addr65:")]) 24 | print(f">>> running cmd=add as ref ~ {elapsed:.2f}s") 25 | 26 | for host in hosts: 27 | print(f">>> running cmd=add {host}... ", end="") 28 | out, elapsed = run(["make", "remote", f"host={host}", "cmd=mul"]) 29 | out = parse_final(out) 30 | addr33 = set([x.strip() for x in out.split("\n") if x.startswith("addr33:")]) 31 | addr65 = set([x.strip() for x in out.split("\n") if x.startswith("addr65:")]) 32 | if ref_out == out and ref_addr33 == addr33 and ref_addr65 == addr65: 33 | print(f"ok ~ {elapsed:.2f}s") 34 | else: 35 | print(f"failed ~ {elapsed:.2f}s") 36 | print(f"{ref_out=} != {out=}") if out != ref_out else None 37 | print(f"{ref_addr33=} != {addr33=}") if addr33 != ref_addr33 else None 38 | print(f"{ref_addr65=} != {addr65=}") if addr65 != ref_addr65 else None 39 | 40 | 41 | def check_mul(hosts: list[str]): 42 | out, elapsed = run(["make", "mul"]) 43 | ref_out = parse_final(out) 44 | print(f">>> running cmd=mul as ref ~ {elapsed:.2f}s") 45 | 46 | for host in hosts: 47 | print(f">>> running cmd=mul {host}... ", end="") 48 | out, elapsed = run(["make", "remote", f"host={host}", "cmd=mul"]) 49 | out = parse_final(out) 50 | if ref_out == out: 51 | print(f"ok ~ {elapsed:.2f}s") 52 | else: 53 | print(f"failed ~ {elapsed:.2f}s") 54 | print(f"{ref_out=} != {out=}") if out != ref_out else None 55 | 56 | 57 | def check_blf(hosts: list[str]): 58 | out, elapsed = run(["make", "blf"]) 59 | ref_out = parse_final(out) 60 | print(f">>> running cmd=blf as ref ~ {elapsed:.2f}s") 61 | 62 | for host in hosts: 63 | print(f">>> running cmd=blf {host}... ", end="") 64 | out, elapsed = run(["make", "remote", f"host={host}", "cmd=blf"]) 65 | out = parse_final(out) 66 | if ref_out == out: 67 | print(f"ok ~ {elapsed}s") 68 | else: 69 | print(f"failed ~ {elapsed}s") 70 | print(f"{ref_out=} != {out=}") if out != ref_out else None 71 | 72 | 73 | def main(): 74 | hosts = ["user@colima", "user@mele.local"] 75 | funcs = [check_add, check_mul, check_blf] 76 | for func in funcs: 77 | print(f"--- {func.__name__} ---") 78 | func(hosts) 79 | 80 | 81 | if __name__ == "__main__": 82 | main() 83 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: default clean build bench fmt add mul rnd blf remote 2 | 3 | CC = cc 4 | CC_FLAGS ?= -O3 -ffast-math -Wall -Wextra 5 | 6 | ifeq ($(shell uname -m),x86_64) 7 | CC_FLAGS += -march=native -pthread -lpthread 8 | endif 9 | 10 | default: build 11 | 12 | clean: 13 | @rm -rf ecloop bench main a.out *.profraw *.profdata 14 | 15 | build: clean 16 | $(CC) $(CC_FLAGS) main.c -o ecloop 17 | 18 | bench: build 19 | ./ecloop bench 20 | 21 | fmt: 22 | @find . -name '*.c' | xargs clang-format -i 23 | 24 | # ----------------------------------------------------------------------------- 25 | 26 | add: build 27 | ./ecloop add -f data/btc-puzzles-hash -r 8000:ffffff 28 | 29 | mul: build 30 | cat data/btc-bw-priv | ./ecloop mul -f data/btc-bw-hash -a cu -q -o /dev/null 31 | 32 | rnd: build 33 | ./ecloop rnd -f data/btc-puzzles-hash -r 800000000000000000:ffffffffffffffffff -d 0:32 34 | 35 | blf: build 36 | @rm -rf /tmp/test.blf 37 | @printf "\n> " 38 | cat data/btc-puzzles-hash | ./ecloop blf-gen -n 32768 -o /tmp/test.blf 39 | @printf "\n> " 40 | cat data/btc-bw-hash | ./ecloop blf-gen -n 32768 -o /tmp/test.blf 41 | @printf "\n> " 42 | ./ecloop add -f /tmp/test.blf -r 8000:ffffff -q -o /dev/null 43 | @printf "\n> " 44 | cat data/btc-bw-priv | ./ecloop mul -f /tmp/test.blf -a cu -q -o /dev/null 45 | 46 | verify: build 47 | ./ecloop mult-verify 48 | 49 | # ----------------------------------------------------------------------------- 50 | # https://btcpuzzle.info/puzzle 51 | 52 | range_28 = 8000000:fffffff 53 | range_32 = 80000000:ffffffff 54 | range_33 = 100000000:1ffffffff 55 | range_34 = 200000000:3ffffffff 56 | range_35 = 400000000:7ffffffff 57 | range_36 = 800000000:fffffffff 58 | range_71 = 400000000000000000:7fffffffffffffffff 59 | range_72 = 800000000000000000:ffffffffffffffffff 60 | range_73 = 1000000000000000000:1ffffffffffffffffff 61 | range_74 = 2000000000000000000:3ffffffffffffffffff 62 | range_76 = 8000000000000000000:fffffffffffffffffff 63 | range_77 = 10000000000000000000:1fffffffffffffffffff 64 | range_78 = 20000000000000000000:3fffffffffffffffffff 65 | range_79 = 40000000000000000000:7fffffffffffffffffff 66 | _RANGES_ = $(foreach r,$(filter range_%,$(.VARIABLES)),$(patsubst range_%,%,$r)) 67 | 68 | puzzle: build 69 | @$(if $(filter $(_RANGES_),$(n)),,$(error "Invalid range $(n)")) 70 | ./ecloop rnd -f data/btc-puzzles-hash -d 0:32 -r $(range_$(n)) -o ./found_$(n).txt 71 | 72 | %: 73 | @$(if $(filter $(_RANGES_),$@),make --no-print-directory puzzle n=$@,) 74 | 75 | # ----------------------------------------------------------------------------- 76 | 77 | host=mele 78 | cmd=add 79 | 80 | remote: 81 | @rsync -arc --progress --delete-after --exclude={'ecloop','found*.txt','.git'} ./ $(host):/tmp/ecloop 82 | @ssh -tt $(host) 'clear; $(CC) --version' 83 | ssh -tt $(host) 'cd /tmp/ecloop; make $(cmd) CC=$(CC)' 84 | 85 | bench-compare: 86 | @ssh -tt $(host) " \ 87 | cd /tmp; rm -rf ecloop keyhunt; \ 88 | cd /tmp && git clone https://github.com/vladkens/ecloop.git && cd ecloop && make CC=clang; \ 89 | echo '--------------------------------------------------'; \ 90 | cd /tmp && git clone https://github.com/albertobsd/keyhunt.git && cd keyhunt && make; \ 91 | echo '--------------------------------------------------'; \ 92 | cd /tmp; \ 93 | echo '--- t=1 (keyhunt)'; \ 94 | time ./keyhunt/keyhunt -m rmd160 -f ecloop/data/btc-bw-hash -r 8000:fffffff -t 1 -n 16777216; \ 95 | echo '--- t=1 (ecloop)'; \ 96 | time ./ecloop/ecloop add -f ecloop/data/btc-bw-hash -t 1 -r 8000:fffffff; \ 97 | echo '--- t=4 (keyhunt)'; \ 98 | time ./keyhunt/keyhunt -m rmd160 -f ecloop/data/btc-bw-hash -r 8000:fffffff -t 4 -n 16777216; \ 99 | echo '--- t=4 (ecloop)'; \ 100 | time ./ecloop/ecloop add -f ecloop/data/btc-bw-hash -t 4 -r 8000:fffffff; \ 101 | " 102 | -------------------------------------------------------------------------------- /lib/addr.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) vladkens 2 | // https://github.com/vladkens/ecloop 3 | // Licensed under the MIT License. 4 | 5 | #pragma once 6 | #include 7 | #include 8 | #include 9 | 10 | #include "ecc.c" 11 | #include "rmd160.c" 12 | #include "rmd160s.c" 13 | #include "sha256.c" 14 | 15 | #define HASH_BATCH_SIZE ((size_t)RMD_LEN) 16 | typedef u32 h160_t[5]; 17 | 18 | int compare_160(const void *a, const void *b) { 19 | const u32 *ua = (const u32 *)a; 20 | const u32 *ub = (const u32 *)b; 21 | for (int i = 0; i < 5; i++) { 22 | if (ua[i] < ub[i]) return -1; 23 | if (ua[i] > ub[i]) return 1; 24 | } 25 | return 0; 26 | } 27 | 28 | void print_h160(const h160_t h) { 29 | for (int i = 0; i < 5; i++) printf("%08x", h[i]); 30 | printf("\n"); 31 | } 32 | 33 | void prepare33(u8 msg[64], const pe *point) { 34 | assert(*point->z == 1); // point should be in affine coordinates 35 | 36 | msg[0] = point->y[0] & 1 ? 0x03 : 0x02; 37 | for (int i = 0; i < 4; i++) { 38 | u64 x_be = swap64(point->x[3 - i]); 39 | memcpy(&msg[1 + i * 8], &x_be, sizeof(u64)); 40 | } 41 | 42 | msg[33] = 0x80; 43 | msg[62] = 0x01; 44 | msg[63] = 0x08; 45 | } 46 | 47 | void prepare65(u8 msg[128], const pe *point) { 48 | assert(*point->z == 1); // point should be in affine coordinates 49 | 50 | msg[0] = 0x04; 51 | 52 | // copy point->x into msg[1..33] in big-endian order 53 | for (int i = 0; i < 4; i++) { 54 | u64 x_be = swap64(point->x[3 - i]); 55 | memcpy(&msg[1 + i * 8], &x_be, sizeof(u64)); 56 | } 57 | 58 | // copy point->y into msg[33..65] in big-endian order 59 | for (int i = 0; i < 4; i++) { 60 | u64 y_be = swap64(point->y[3 - i]); 61 | memcpy(&msg[33 + i * 8], &y_be, sizeof(u64)); 62 | } 63 | 64 | msg[65] = 0x80; 65 | msg[126] = 0x02; 66 | msg[127] = 0x08; 67 | } 68 | 69 | void prepare_rmd(u32 rs[16]) { 70 | for (int i = 0; i < 8; i++) rs[i] = swap32(rs[i]); 71 | rs[8] = 0x00000080; 72 | rs[14] = 256; 73 | } 74 | 75 | void addr33(u32 r[5], const pe *point) { 76 | u8 msg[64] = {0}; // sha256 payload 77 | u32 rs[16] = {0}; // sha256 output and rmd160 input 78 | 79 | prepare33(msg, point); 80 | sha256_final(rs, msg, sizeof(msg)); 81 | 82 | prepare_rmd(rs); 83 | rmd160_final(r, rs); 84 | } 85 | 86 | void addr65(u32 *r, const pe *point) { 87 | u8 msg[128] = {0}; // sha256 payload 88 | u32 rs[16] = {0}; // sha256 output and rmd160 input 89 | 90 | prepare65(msg, point); 91 | sha256_final(rs, msg, sizeof(msg)); 92 | 93 | prepare_rmd(rs); 94 | rmd160_final(r, rs); 95 | } 96 | 97 | // MARK: SIMD 98 | 99 | void addr33_batch(h160_t *hashes, const pe *points, size_t count) { 100 | assert(count <= HASH_BATCH_SIZE); 101 | u8 msg[HASH_BATCH_SIZE][64] = {0}; // sha256 payload 102 | u32 rs[HASH_BATCH_SIZE][16] = {0}; // sha256 output and rmd160 input 103 | 104 | for (size_t i = 0; i < count; ++i) prepare33(msg[i], points + i); 105 | for (size_t i = 0; i < count; ++i) sha256_final(rs[i], msg[i], sizeof(msg[i])); 106 | 107 | // for (size_t i = 0; i < count; ++i) prepare_rmd(rs[i]); 108 | for (size_t i = 0; i < count; ++i) { 109 | rs[i][8] = 0x80000000; // 80 in little-endian 110 | rs[i][14] = 0x00010000; // 256 in little-endian 111 | } 112 | 113 | rmd160_batch(hashes, rs); 114 | } 115 | 116 | void addr65_batch(h160_t *hashes, const pe *points, size_t count) { 117 | assert(count <= HASH_BATCH_SIZE); 118 | u8 msg[HASH_BATCH_SIZE][128] = {0}; // sha256 payload 119 | u32 rs[HASH_BATCH_SIZE][16] = {0}; // sha256 output and rmd160 input 120 | 121 | for (size_t i = 0; i < count; ++i) prepare65(msg[i], points + i); 122 | for (size_t i = 0; i < count; ++i) sha256_final(rs[i], msg[i], sizeof(msg[i])); 123 | 124 | // for (size_t i = 0; i < count; ++i) prepare_rmd(rs[i]); 125 | for (size_t i = 0; i < count; ++i) { 126 | rs[i][8] = 0x80000000; // 80 in little-endian 127 | rs[i][14] = 0x00010000; // 256 in little-endian 128 | } 129 | 130 | rmd160_batch(hashes, rs); 131 | } 132 | -------------------------------------------------------------------------------- /lib/compat.c: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define USE_BUILTIN 1 4 | #define HAS_BUILTIN(fn) (USE_BUILTIN && __has_builtin(fn)) 5 | 6 | typedef __uint128_t u128; 7 | typedef unsigned long long u64; 8 | typedef unsigned int u32; 9 | typedef unsigned char u8; 10 | #define INLINE static inline __attribute__((always_inline)) 11 | 12 | // https://stackoverflow.com/a/32107675/3664464 13 | 14 | #define MIN(x, y) \ 15 | ({ \ 16 | __auto_type _x = (x); \ 17 | __auto_type _y = (y); \ 18 | _x < _y ? _x : _y; \ 19 | }) 20 | 21 | #define MAX(x, y) \ 22 | ({ \ 23 | __auto_type _x = (x); \ 24 | __auto_type _y = (y); \ 25 | _x > _y ? _x : _y; \ 26 | }) 27 | 28 | // https://clang.llvm.org/docs/LanguageExtensions.html#:~:text=__builtin_addcll 29 | // https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html#:~:text=__builtin_uaddll_overflow 30 | 31 | #if HAS_BUILTIN(__builtin_addcll) 32 | #define addc64(x, y, carryin, carryout) __builtin_addcll(x, y, carryin, carryout) 33 | #else 34 | #define addc64(x, y, carryin, carryout) \ 35 | ({ \ 36 | u64 rs; \ 37 | bool overflow1 = __builtin_uaddll_overflow(x, y, &rs); \ 38 | bool overflow2 = __builtin_uaddll_overflow(rs, carryin, &rs); \ 39 | *(carryout) = (overflow1 || overflow2) ? 1 : 0; \ 40 | rs; \ 41 | }) 42 | #endif 43 | 44 | // https://clang.llvm.org/docs/LanguageExtensions.html#:~:text=__builtin_subcll 45 | // https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html#:~:text=__builtin_usubll_overflow 46 | 47 | #if HAS_BUILTIN(__builtin_subcll) 48 | #define subc64(x, y, carryin, carryout) __builtin_subcll(x, y, carryin, carryout) 49 | #else 50 | #define subc64(x, y, carryin, carryout) \ 51 | ({ \ 52 | u64 rs; \ 53 | bool underflow1 = __builtin_usubll_overflow(x, y, &rs); \ 54 | bool underflow2 = __builtin_usubll_overflow(rs, carryin, &rs); \ 55 | *(carryout) = (underflow1 || underflow2) ? 1 : 0; \ 56 | rs; \ 57 | }) 58 | #endif 59 | 60 | // Other builtins 61 | 62 | #if HAS_BUILTIN(__builtin_rotateleft32) 63 | #define rotl32(x, n) __builtin_rotateleft32(x, n) 64 | #else 65 | #define rotl32(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) 66 | #endif 67 | 68 | #if HAS_BUILTIN(__builtin_bswap32) 69 | #define swap32(x) __builtin_bswap32(x) 70 | #else 71 | #define swap32(x) ((x) << 24) | ((x) << 8 & 0x00ff0000) | ((x) >> 8 & 0x0000ff00) | ((x) >> 24) 72 | #endif 73 | 74 | #if HAS_BUILTIN(__builtin_bswap64) 75 | #define swap64(x) __builtin_bswap64(x) 76 | #else 77 | #define swap64(x) \ 78 | ((x) << 56) | ((x) << 40 & 0x00ff000000000000) | ((x) << 24 & 0x0000ff0000000000) | \ 79 | ((x) << 8 & 0x000000ff00000000) | ((x) >> 8 & 0x00000000ff000000) | \ 80 | ((x) >> 24 & 0x0000000000ff0000) | ((x) >> 40 & 0x000000000000ff00) | ((x) >> 56) 81 | #endif 82 | -------------------------------------------------------------------------------- /lib/rmd160.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) vladkens 2 | // https://github.com/vladkens/ecloop 3 | // Licensed under the MIT License. 4 | 5 | #pragma once 6 | #include "compat.c" 7 | 8 | static const u8 _n[80] = { 9 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 10 | 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, // 11 | 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, // 12 | 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, // 13 | 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13, // 14 | }; 15 | 16 | static const u8 _r[80] = { 17 | 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, // 18 | 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, // 19 | 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, // 20 | 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, // 21 | 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6, // 22 | }; 23 | 24 | static const u8 n_[80] = { 25 | 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, // 26 | 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, // 27 | 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, // 28 | 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, // 29 | 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11, // 30 | }; 31 | 32 | static const u8 r_[80] = { 33 | 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, // 34 | 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, // 35 | 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, // 36 | 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, // 37 | 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11, // 38 | }; 39 | 40 | #define F1(x, y, z) ((x) ^ (y) ^ (z)) 41 | #define F2(x, y, z) (((x) & (y)) | (~(x) & (z))) 42 | #define F3(x, y, z) (((x) | ~(y)) ^ (z)) 43 | #define F4(x, y, z) (((x) & (z)) | ((y) & ~(z))) 44 | #define F5(x, y, z) ((x) ^ ((y) | ~(z))) 45 | 46 | void rmd160_final(u32 state[5], const u32 x[16]) { 47 | u32 a1, b1, c1, d1, e1, a2, b2, c2, d2, e2; 48 | state[0] = a1 = a2 = 0x67452301; 49 | state[1] = b1 = b2 = 0xefcdab89; 50 | state[2] = c1 = c2 = 0x98badcfe; 51 | state[3] = d1 = d2 = 0x10325476; 52 | state[4] = e1 = e2 = 0xc3d2e1f0; 53 | 54 | u32 alpha, beta; 55 | u32 i = 0; 56 | 57 | // round 1 58 | for (; i < 16; ++i) { 59 | alpha = a1 + F1(b1, c1, d1) + x[_n[i]]; 60 | alpha = rotl32(alpha, _r[i]) + e1; 61 | beta = rotl32(c1, 10); 62 | a1 = e1, c1 = b1, e1 = d1, b1 = alpha, d1 = beta; 63 | 64 | alpha = a2 + F5(b2, c2, d2) + x[n_[i]] + 0x50a28be6; 65 | alpha = rotl32(alpha, r_[i]) + e2; 66 | beta = rotl32(c2, 10); 67 | a2 = e2, c2 = b2, e2 = d2, b2 = alpha, d2 = beta; 68 | } 69 | 70 | // round 2 71 | for (; i < 32; ++i) { 72 | alpha = a1 + F2(b1, c1, d1) + x[_n[i]] + 0x5a827999; 73 | alpha = rotl32(alpha, _r[i]) + e1; 74 | beta = rotl32(c1, 10); 75 | a1 = e1, c1 = b1, e1 = d1, b1 = alpha, d1 = beta; 76 | 77 | alpha = a2 + F4(b2, c2, d2) + x[n_[i]] + 0x5c4dd124; 78 | alpha = rotl32(alpha, r_[i]) + e2; 79 | beta = rotl32(c2, 10); 80 | a2 = e2, c2 = b2, e2 = d2, b2 = alpha, d2 = beta; 81 | } 82 | 83 | // round 3 84 | for (; i < 48; ++i) { 85 | alpha = a1 + F3(b1, c1, d1) + x[_n[i]] + 0x6ed9eba1; 86 | alpha = rotl32(alpha, _r[i]) + e1; 87 | beta = rotl32(c1, 10); 88 | a1 = e1, c1 = b1, e1 = d1, b1 = alpha, d1 = beta; 89 | 90 | alpha = a2 + F3(b2, c2, d2) + x[n_[i]] + 0x6d703ef3; 91 | alpha = rotl32(alpha, r_[i]) + e2; 92 | beta = rotl32(c2, 10); 93 | a2 = e2, c2 = b2, e2 = d2, b2 = alpha, d2 = beta; 94 | } 95 | 96 | // round 4 97 | for (; i < 64; ++i) { 98 | alpha = a1 + F4(b1, c1, d1) + x[_n[i]] + 0x8f1bbcdc; 99 | alpha = rotl32(alpha, _r[i]) + e1; 100 | beta = rotl32(c1, 10); 101 | a1 = e1, c1 = b1, e1 = d1, b1 = alpha, d1 = beta; 102 | 103 | alpha = a2 + F2(b2, c2, d2) + x[n_[i]] + 0x7a6d76e9; 104 | alpha = rotl32(alpha, r_[i]) + e2; 105 | beta = rotl32(c2, 10); 106 | a2 = e2, c2 = b2, e2 = d2, b2 = alpha, d2 = beta; 107 | } 108 | 109 | // round 5 110 | for (; i < 80; ++i) { 111 | alpha = a1 + F5(b1, c1, d1) + x[_n[i]] + 0xa953fd4e; 112 | alpha = rotl32(alpha, _r[i]) + e1; 113 | beta = rotl32(c1, 10); 114 | a1 = e1, c1 = b1, e1 = d1, b1 = alpha, d1 = beta; 115 | 116 | alpha = a2 + F1(b2, c2, d2) + x[n_[i]]; 117 | alpha = rotl32(alpha, r_[i]) + e2; 118 | beta = rotl32(c2, 10); 119 | a2 = e2, c2 = b2, e2 = d2, b2 = alpha, d2 = beta; 120 | } 121 | 122 | d2 += c1 + state[1]; 123 | state[1] = state[2] + d1 + e2; 124 | state[2] = state[3] + e1 + a2; 125 | state[3] = state[4] + a1 + b2; 126 | state[4] = state[0] + b1 + c2; 127 | state[0] = d2; 128 | 129 | for (int i = 0; i < 5; ++i) state[i] = swap32(state[i]); 130 | } 131 | -------------------------------------------------------------------------------- /lib/bench.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) vladkens 2 | // https://github.com/vladkens/ecloop 3 | // Licensed under the MIT License. 4 | 5 | #pragma once 6 | #include 7 | 8 | #include "addr.c" 9 | #include "ecc.c" 10 | #include "utils.c" 11 | 12 | void print_res(char *label, size_t stime, size_t iters) { 13 | double dt = MAX((tsnow() - stime), 1ul) / 1000.0; 14 | printf("%20s: %.2fM it/s ~ %.2fs\n", label, iters / dt / 1000000, dt); 15 | } 16 | 17 | void run_bench() { 18 | ec_gtable_init(); 19 | 20 | // note: asserts used to prevent compiler optimization 21 | size_t stime, iters, i; 22 | pe g; 23 | fe f; 24 | 25 | // projective & jacobian coordinates 26 | iters = 1000 * 1000 * 6; 27 | 28 | stime = tsnow(); 29 | pe_clone(&g, &G2); 30 | for (i = 0; i < iters; ++i) _ec_jacobi_add1(&g, &g, &G1); 31 | print_res("_ec_jacobi_add1", stime, iters); 32 | assert(fe_cmp(g.x, G1.x) != 0); 33 | 34 | pe_clone(&g, &G2); 35 | stime = tsnow(); 36 | for (i = 0; i < iters; ++i) _ec_jacobi_add2(&g, &g, &G1); 37 | print_res("_ec_jacobi_add2", stime, iters); 38 | assert(fe_cmp(g.x, G1.x) != 0); 39 | 40 | pe_clone(&g, &G2); 41 | stime = tsnow(); 42 | for (i = 0; i < iters; ++i) _ec_jacobi_dbl1(&g, &g); 43 | print_res("_ec_jacobi_dbl1", stime, iters); 44 | assert(fe_cmp(g.x, G1.x) != 0); 45 | 46 | pe_clone(&g, &G2); 47 | stime = tsnow(); 48 | for (i = 0; i < iters; ++i) _ec_jacobi_dbl2(&g, &g); 49 | print_res("_ec_jacobi_dbl2", stime, iters); 50 | assert(fe_cmp(g.x, G1.x) != 0); 51 | 52 | // ec multiplication 53 | srand(42); 54 | size_t numSize = 1024 * 16; 55 | fe numbers[numSize]; 56 | for (size_t i = 0; i < numSize; ++i) fe_prand(numbers[i]); 57 | pe_clone(&g, &G2); 58 | 59 | iters = 1000 * 10; 60 | stime = tsnow(); 61 | for (i = 0; i < iters; ++i) ec_jacobi_mul(&g, &G1, numbers[i % numSize]); 62 | print_res("ec_jacobi_mul", stime, iters); 63 | assert(fe_cmp(g.x, G1.x) != 0); 64 | 65 | iters = 1000 * 500; 66 | stime = tsnow(); 67 | for (i = 0; i < iters; ++i) ec_gtable_mul(&g, numbers[i % numSize]); 68 | print_res("ec_gtable_mul", stime, iters); 69 | assert(fe_cmp(g.x, G1.x) != 0); 70 | 71 | // affine coordinates 72 | iters = 1000 * 500; 73 | 74 | pe_clone(&g, &G2); 75 | stime = tsnow(); 76 | for (i = 0; i < iters; ++i) ec_affine_add(&g, &g, &G1); 77 | print_res("ec_affine_add", stime, iters); 78 | assert(fe_cmp(g.x, G1.x) != 0); 79 | 80 | pe_clone(&g, &G2); 81 | stime = tsnow(); 82 | for (i = 0; i < iters; ++i) ec_affine_dbl(&g, &g); 83 | print_res("ec_affine_dbl", stime, iters); 84 | assert(fe_cmp(g.x, G1.x) != 0); 85 | 86 | // modular inversion 87 | iters = 1000 * 100; 88 | 89 | stime = tsnow(); 90 | for (i = 0; i < iters; ++i) _fe_modp_inv_binpow(f, g.x); 91 | print_res("_fe_modinv_binpow", stime, iters); 92 | assert(fe_cmp(f, G1.x) != 0); 93 | 94 | stime = tsnow(); 95 | for (i = 0; i < iters; ++i) _fe_modp_inv_addchn(f, g.x); 96 | print_res("_fe_modinv_addchn", stime, iters); 97 | assert(fe_cmp(f, G1.x) != 0); 98 | 99 | // hash functions 100 | iters = 1000 * 1000 * 10; 101 | h160_t h160; 102 | 103 | stime = tsnow(); 104 | for (i = 0; i < iters; ++i) addr33(h160, &g); 105 | print_res("addr33", stime, iters); 106 | assert(h160[0] != 0); 107 | 108 | stime = tsnow(); 109 | for (i = 0; i < iters; ++i) addr65(h160, &g); 110 | print_res("addr65", stime, iters); 111 | assert(h160[0] != 0); 112 | } 113 | 114 | void run_bench_gtable() { 115 | srand(42); 116 | size_t numSize = 1024 * 16; 117 | fe numbers[numSize]; 118 | for (size_t i = 0; i < numSize; ++i) fe_prand(numbers[i]); 119 | 120 | size_t iters = 1000 * 500; 121 | size_t stime; 122 | double gent, mult; 123 | pe g; 124 | 125 | size_t mem_used; 126 | for (int i = 8; i <= 22; i += 2) { 127 | _GTABLE_W = i; 128 | 129 | stime = tsnow(); 130 | mem_used = ec_gtable_init(); 131 | gent = ((double)(tsnow() - stime)) / 1000; 132 | 133 | stime = tsnow(); 134 | for (size_t i = 0; i < iters; ++i) ec_gtable_mul(&g, numbers[i % numSize]); 135 | mult = ((double)(tsnow() - stime)) / 1000; 136 | 137 | double mem = (double)mem_used / 1024 / 1024; // MB 138 | printf("w=%02d: %.1fK it/s | gen: %5.2fs | mul: %5.2fs | mem: %8.1fMB\n", // 139 | i, iters / mult / 1000, gent, mult, mem); 140 | } 141 | } 142 | 143 | void mult_verify() { 144 | ec_gtable_init(); 145 | 146 | pe r1, r2; 147 | fe pk; 148 | for (int i = 0; i < 1000 * 16; ++i) { 149 | fe_set64(pk, i + 2); 150 | 151 | ec_jacobi_mulrdc(&r1, &G1, pk); 152 | ec_verify(&r1); 153 | 154 | ec_gtable_mul(&r2, pk); 155 | ec_jacobi_rdc(&r2, &r2); 156 | ec_verify(&r2); 157 | 158 | if (memcmp(&r1, &r2, sizeof(pe)) != 0) { 159 | printf("invalid on %d\n", i); 160 | fe_print("pk", pk); 161 | fe_print("r1", r1.x); 162 | fe_print("r2", r2.x); 163 | exit(1); 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /data/btc-puzzles-addr: -------------------------------------------------------------------------------- 1 | 1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH 2 | 1CUNEBjYrCn2y1SdiUMohaKUi4wpP326Lb 3 | 19ZewH8Kk1PDbSNdJ97FP4EiCjTRaZMZQA 4 | 1EhqbyUMvvs7BfL8goY6qcPbD6YKfPqb7e 5 | 1E6NuFjCi27W5zoXg8TRdcSRq84zJeBW3k 6 | 1PitScNLyp2HCygzadCh7FveTnfmpPbfp8 7 | 1McVt1vMtCC7yn5b9wgX1833yCcLXzueeC 8 | 1M92tSqNmQLYw33fuBvjmeadirh1ysMBxK 9 | 1CQFwcjw1dwhtkVWBttNLDtqL7ivBonGPV 10 | 1LeBZP5QCwwgXRtmVUvTVrraqPUokyLHqe 11 | 1PgQVLmst3Z314JrQn5TNiys8Hc38TcXJu 12 | 1DBaumZxUkM4qMQRt2LVWyFJq5kDtSZQot 13 | 1Pie8JkxBT6MGPz9Nvi3fsPkr2D8q3GBc1 14 | 1ErZWg5cFCe4Vw5BzgfzB74VNLaXEiEkhk 15 | 1QCbW9HWnwQWiQqVo5exhAnmfqKRrCRsvW 16 | 1BDyrQ6WoF8VN3g9SAS1iKZcPzFfnDVieY 17 | 1HduPEXZRdG26SUT5Yk83mLkPyjnZuJ7Bm 18 | 1GnNTmTVLZiqQfLbAdp9DVdicEnB5GoERE 19 | 1NWmZRpHH4XSPwsW6dsS3nrNWfL1yrJj4w 20 | 1HsMJxNiV7TLxmoF6uJNkydxPFDog4NQum 21 | 14oFNXucftsHiUMY8uctg6N487riuyXs4h 22 | 1CfZWK1QTQE3eS9qn61dQjV89KDjZzfNcv 23 | 1L2GM8eE7mJWLdo3HZS6su1832NX2txaac 24 | 1rSnXMr63jdCuegJFuidJqWxUPV7AtUf7 25 | 15JhYXn6Mx3oF4Y7PcTAv2wVVAuCFFQNiP 26 | 1JVnST957hGztonaWK6FougdtjxzHzRMMg 27 | 128z5d7nN7PkCuX5qoA4Ys6pmxUYnEy86k 28 | 12jbtzBb54r97TCwW3G1gCFoumpckRAPdY 29 | 19EEC52krRUK1RkUAEZmQdjTyHT7Gp1TYT 30 | 1LHtnpd8nU5VHEMkG2TMYYNUjjLc992bps 31 | 1LhE6sCTuGae42Axu1L1ZB7L96yi9irEBE 32 | 1FRoHA9xewq7DjrZ1psWJVeTer8gHRqEvR 33 | 187swFMjz1G54ycVU56B7jZFHFTNVQFDiu 34 | 1PWABE7oUahG2AFFQhhvViQovnCr4rEv7Q 35 | 1PWCx5fovoEaoBowAvF5k91m2Xat9bMgwb 36 | 1Be2UF9NLfyLFbtm3TCbmuocc9N1Kduci1 37 | 14iXhn8bGajVWegZHJ18vJLHhntcpL4dex 38 | 1HBtApAFA9B2YZw3G2YKSMCtb3dVnjuNe2 39 | 122AJhKLEfkFBaGAd84pLp1kfE7xK3GdT8 40 | 1EeAxcprB2PpCnr34VfZdFrkUWuxyiNEFv 41 | 1L5sU9qvJeuwQUdt4y1eiLmquFxKjtHr3E 42 | 1E32GPWgDyeyQac4aJxm9HVoLrrEYPnM4N 43 | 1PiFuqGpG8yGM5v6rNHWS3TjsG6awgEGA1 44 | 1CkR2uS7LmFwc3T2jV8C1BhWb5mQaoxedF 45 | 1NtiLNGegHWE3Mp9g2JPkgx6wUg4TW7bbk 46 | 1F3JRMWudBaj48EhwcHDdpeuy2jwACNxjP 47 | 1Pd8VvT49sHKsmqrQiP61RsVwmXCZ6ay7Z 48 | 1DFYhaB2J9q1LLZJWKTnscPWos9VBqDHzv 49 | 12CiUhYVTTH33w3SPUBqcpMoqnApAV4WCF 50 | 1MEzite4ReNuWaL5Ds17ePKt2dCxWEofwk 51 | 1NpnQyZ7x24ud82b7WiRNvPm6N8bqGQnaS 52 | 15z9c9sVpu6fwNiK7dMAFgMYSK4GqsGZim 53 | 15K1YKJMiJ4fpesTVUcByoz334rHmknxmT 54 | 1KYUv7nSvXx4642TKeuC2SNdTk326uUpFy 55 | 1LzhS3k3e9Ub8i2W1V8xQFdB8n2MYCHPCa 56 | 17aPYR1m6pVAacXg1PTDDU7XafvK1dxvhi 57 | 15c9mPGLku1HuW9LRtBf4jcHVpBUt8txKz 58 | 1Dn8NF8qDyyfHMktmuoQLGyjWmZXgvosXf 59 | 1HAX2n9Uruu9YDt4cqRgYcvtGvZj1rbUyt 60 | 1Kn5h2qpgw9mWE5jKpk8PP4qvvJ1QVy8su 61 | 1AVJKwzs9AskraJLGHAZPiaZcrpDr1U6AB 62 | 1Me6EfpwZK5kQziBwBfvLiHjaPGxCKLoJi 63 | 1NpYjtLira16LfGbGwZJ5JbDPh3ai9bjf4 64 | 16jY7qLJnxb7CHZyqBP8qca9d51gAjyXQN 65 | 18ZMbwUFLMHoZBbfpCjUJQTCMCbktshgpe 66 | 13zb1hQbWVsc2S7ZTZnP2G4undNNpdh5so 67 | 1BY8GQbnueYofwSuFAT3USAhGjPrkxDdW9 68 | 1MVDYgVaSN6iKKEsbzRUAYFrYJadLYZvvZ 69 | 19vkiEajfhuZ8bs8Zu2jgmC6oqZbWqhxhG 70 | 19YZECXj3SxEZMoUeJ1yiPsw8xANe7M7QR 71 | 1PWo3JeB9jrGwfHDNpdGK54CRas7fsVzXU 72 | 1JTK7s9YVYywfm5XUH7RNhHJH1LshCaRFR 73 | 12VVRNPi4SJqUTsp6FmqDqY5sGosDtysn4 74 | 1FWGcVDK3JGzCC3WtkYetULPszMaK2Jksv 75 | 1J36UjUByGroXcCvmj13U6uwaVv9caEeAt 76 | 1DJh2eHFYQfACPmrvpyWc8MSTYKh7w9eRF 77 | 1Bxk4CQdqL9p22JEtDfdXMsng1XacifUtE 78 | 15qF6X51huDjqTmF9BJgxXdt1xcj46Jmhb 79 | 1ARk8HWJMn8js8tQmGUJeQHjSE7KRkn2t8 80 | 1BCf6rHUW6m3iH2ptsvnjgLruAiPQQepLe 81 | 15qsCm78whspNQFydGJQk5rexzxTQopnHZ 82 | 13zYrYhhJxp6Ui1VV7pqa5WDhNWM45ARAC 83 | 14MdEb4eFcT3MVG5sPFG4jGLuHJSnt1Dk2 84 | 1CMq3SvFcVEcpLMuuH8PUcNiqsK1oicG2D 85 | 1Kh22PvXERd2xpTQk3ur6pPEqFeckCJfAr 86 | 1K3x5L6G57Y494fDqBfrojD28UJv4s5JcK 87 | 1PxH3K1Shdjb7gSEoTX7UPDZ6SH4qGPrvq 88 | 16AbnZjZZipwHMkYKBSfswGWKDmXHjEpSf 89 | 19QciEHbGVNY4hrhfKXmcBBCrJSBZ6TaVt 90 | 1L12FHH2FHjvTviyanuiFVfmzCy46RRATU 91 | 1EzVHtmbN4fs4MiNk3ppEnKKhsmXYJ4s74 92 | 1AE8NzzgKE7Yhz7BWtAcAAxiFMbPo82NB5 93 | 17Q7tuG2JwFFU9rXVj3uZqRtioH3mx2Jad 94 | 1K6xGMUbs6ZTXBnhw1pippqwK6wjBWtNpL 95 | 19eVSDuizydXxhohGh8Ki9WY9KsHdSwoQC 96 | 15ANYzzCp5BFHcCnVFzXqyibpzgPLWaD8b 97 | 18ywPwj39nGjqBrQJSzZVq2izR12MDpDr8 98 | 1CaBVPrwUxbQYYswu32w7Mj4HR4maNoJSX 99 | 1JWnE6p6UN7ZJBN7TtcbNDoRcjFtuDWoNL 100 | 1KCgMv8fo2TPBpddVi9jqmMmcne9uSNJ5F 101 | 1CKCVdbDJasYmhswB6HKZHEAnNaDpK7W4n 102 | 1PXv28YxmYMaB8zxrKeZBW8dt2HK7RkRPX 103 | 1AcAmB6jmtU6AiEcXkmiNE9TNVPsj9DULf 104 | 1EQJvpsmhazYCcKX5Au6AZmZKRnzarMVZu 105 | 1CMjscKB3QW7SDyQ4c3C3DEUHiHRhiZVib 106 | 18KsfuHuzQaBTNLASyj15hy4LuqPUo1FNB 107 | 15EJFC5ZTs9nhsdvSUeBXjLAuYq3SWaxTc 108 | 1HB1iKUqeffnVsvQsbpC6dNi1XKbyNuqao 109 | 1GvgAXVCbA8FBjXfWiAms4ytFeJcKsoyhL 110 | 12JzYkkN76xkwvcPT6AWKZtGX6w2LAgsJg 111 | 1824ZJQ7nKJ9QFTRBqn7z7dHV5EGpzUpH3 112 | 18A7NA9FTsnJxWgkoFfPAFbQzuQxpRtCos 113 | 1NeGn21dUDDeqFQ63xb2SpgUuXuBLA4WT4 114 | 174SNxfqpdMGYy5YQcfLbSTK3MRNZEePoy 115 | 1NLbHuJebVwUZ1XqDjsAyfTRUPwDQbemfv 116 | 1MnJ6hdhvK37VLmqcdEwqC3iFxyWH2PHUV 117 | 1KNRfGWw7Q9Rmwsc6NT5zsdvEb9M2Wkj5Z 118 | 1PJZPzvGX19a7twf5HyD2VvNiPdHLzm9F6 119 | 1GuBBhf61rnvRe4K8zu8vdQB3kHzwFqSy7 120 | 17s2b9ksz5y7abUm92cHwG8jEPCzK3dLnT 121 | 1GDSuiThEV64c166LUFC9uDcVdGjqkxKyh 122 | 1Me3ASYt5JCTAK2XaC32RMeH34PdprrfDx 123 | 1CdufMQL892A69KXgv6UNBD17ywWqYpKut 124 | 1BkkGsX9ZM6iwL3zbqs7HWBV7SvosR6m8N 125 | 1PXAyUB8ZoH3WD8n5zoAthYjN15yN5CVq5 126 | 1AWCLZAjKbV1P7AHvaPNCKiB7ZWVDMxFiz 127 | 1G6EFyBRU86sThN3SSt3GrHu1sA7w7nzi4 128 | 1MZ2L1gFrCtkkn6DnTT2e4PFUTHw9gNwaj 129 | 1Hz3uv3nNZzBVMXLGadCucgjiCs5W9vaGz 130 | 1Fo65aKq8s8iquMt6weF1rku1moWVEd5Ua 131 | 16zRPnT8znwq42q7XeMkZUhb1bKqgRogyy 132 | 1KrU4dHE5WrW8rhWDsTRjR21r8t3dsrS3R 133 | 17uDfp5r4n441xkgLFmhNoSW1KWp6xVLD 134 | 13A3JrvXmvg5w9XGvyyR4JEJqiLz8ZySY3 135 | 16RGFo6hjq9ym6Pj7N5H7L1NR1rVPJyw2v 136 | 1UDHPdovvR985NrWSkdWQDEQ1xuRiTALq 137 | 15nf31J46iLuK1ZkTnqHo7WgN5cARFK3RA 138 | 1Ab4vzG6wEQBDNQM1B2bvUz4fqXXdFk2WT 139 | 1Fz63c775VV9fNyj25d9Xfw3YHE6sKCxbt 140 | 1QKBaU6WAeycb3DbKbLBkX7vJiaS8r42Xo 141 | 1CD91Vm97mLQvXhrnoMChhJx4TP9MaQkJo 142 | 15MnK2jXPqTMURX4xC3h4mAZxyCcaWWEDD 143 | 13N66gCzWWHEZBxhVxG18P8wyjEWF9Yoi1 144 | 1NevxKDYuDcCh1ZMMi6ftmWwGrZKC6j7Ux 145 | 19GpszRNUej5yYqxXoLnbZWKew3KdVLkXg 146 | 1M7ipcdYHey2Y5RZM34MBbpugghmjaV89P 147 | 18aNhurEAJsw6BAgtANpexk5ob1aGTwSeL 148 | 1FwZXt6EpRT7Fkndzv6K4b4DFoT4trbMrV 149 | 1CXvTzR6qv8wJ7eprzUKeWxyGcHwDYP1i2 150 | 1MUJSJYtGPVGkBCTqGspnxyHahpt5Te8jy 151 | 13Q84TNNvgcL3HJiqQPvyBb9m4hxjS3jkV 152 | 1LuUHyrQr8PKSvbcY1v1PiuGuqFjWpDumN 153 | 18192XpzzdDi2K11QVHR7td2HcPS6Qs5vg 154 | 1NgVmsCCJaKLzGyKLFJfVequnFW9ZvnMLN 155 | 1AoeP37TmHdFh8uN72fu9AqgtLrUwcv2wJ 156 | 1FTpAbQa4h8trvhQXjXnmNhqdiGBd1oraE 157 | 14JHoRAdmJg3XR4RjMDh6Wed6ft6hzbQe9 158 | 19z6waranEf8CcP8FqNgdwUe1QRxvUNKBG 159 | 14u4nA5sugaswb6SZgn5av2vuChdMnD9E5 160 | 1NBC8uXJy1GiJ6drkiZa1WuKn51ps7EPTv 161 | -------------------------------------------------------------------------------- /data/btc-puzzles-hash: -------------------------------------------------------------------------------- 1 | 751e76e8199196d454941c45d1b3a323f1433bd6 2 | 7dd65592d0ab2fe0d0257d571abf032cd9db93dc 3 | 5dedfbf9ea599dd4e3ca6a80b333c472fd0b3f69 4 | 9652d86bedf43ad264362e6e6eba6eb764508127 5 | 8f9dff39a81ee4abcbad2ad8bafff090415a2be8 6 | f93ec34e9e34a8f8ff7d600cdad83047b1bcb45c 7 | e2192e8a7dd8dd1c88321959b477968b941aa973 8 | dce76b2613052ea012204404a97b3c25eac31715 9 | 7d0f6c64afb419bbd7e971e943d7404b0e0daab4 10 | d7729816650e581d7462d52ad6f732da0e2ec93b 11 | f8c698da3164ef8fa4258692d118cc9a902c5acc 12 | 85a1f9ba4da24c24e582d9b891dacbd1b043f971 13 | f932d0188616c964416b91fb9cf76ba9790a921e 14 | 97f9281a1383879d72ac52a6a3e9e8b9a4a4f655 15 | fe7c45126731f7384640b0b0045fd40bac72e2a2 16 | 7025b4efb3ff42eb4d6d71fab6b53b4f4967e3dd 17 | b67cb6edeabc0c8b927c9ea327628e7aa63e2d52 18 | ad1e852b08eba53df306ec9daa8c643426953f94 19 | ebfbe6819fcdebab061732ce91df7d586a037dee 20 | b907c3a2a3b27789dfb509b730dd47703c272868 21 | 29a78213caa9eea824acf08022ab9dfc83414f56 22 | 7ff45303774ef7a52fffd8011981034b258cb86b 23 | d0a79df189fe1ad5c306cc70497b358415da579e 24 | 0959e80121f36aea13b3bad361c15dac26189e2f 25 | 2f396b29b27324300d0c59b17c3abc1835bd3dbb 26 | bfebb73562d4541b32a02ba664d140b5a574792f 27 | 0c7aaf6caa7e5424b63d317f0f8f1f9fa40d5560 28 | 1306b9e4ff56513a476841bac7ba48d69516b1da 29 | 5a416cc9148f4a377b672c8ae5d3287adaafadec 30 | d39c4704664e1deb76c9331e637564c257d68a08 31 | d805f6f251f7479ebd853b3d0f4b9b2656d92f1d 32 | 9e42601eeaedc244e15f17375adb0e2cd08efdc9 33 | 4e15e5189752d1eaf444dfd6bff399feb0443977 34 | f6d67d7983bf70450f295c9cb828daab265f1bfa 35 | f6d8ce225ffbdecec170f8298c3fc28ae686df25 36 | 74b1e012be1521e5d8d75e745a26ced845ea3d37 37 | 28c30fb9118ed1da72e7c4f89c0164756e8a021d 38 | b190e2d40cfdeee2cee072954a2be89e7ba39364 39 | 0b304f2a79a027270276533fe1ed4eff30910876 40 | 95a156cd21b4a69de969eb6716864f4c8b82a82a 41 | d1562eb37357f9e6fc41cb2359f4d3eda4032329 42 | 8efb85f9c5b5db2d55973a04128dc7510075ae23 43 | f92044c7924e5525c61207972c253c9fc9f086f7 44 | 80df54e1f612f2fc5bdc05c9d21a83aa8d20791e 45 | f0225bfc68a6e17e87cd8b5e60ae3be18f120753 46 | 9a012260d01c5113df66c8a8438c9f7a1e3d5dac 47 | f828005d41b0f4fed4c8dca3b06011072cfb07d4 48 | 8661cb56d9df0a61f01328b55af7e56a3fe7a2b2 49 | 0d2f533966c6578e1111978ca698f8add7fffdf3 50 | de081b76f840e462fa2cdf360173dfaf4a976a47 51 | ef6419cffd7fad7027994354eb8efae223c2dbe7 52 | 36af659edbe94453f6344e920d143f1778653ae7 53 | 2f4870ef54fa4b048c1365d42594cc7d3d269551 54 | cb66763cf7fde659869ae7f06884d9a0f879a092 55 | db53d9bbd1f3a83b094eeca7dd970bd85b492fa2 56 | 48214c5969ae9f43f75070cea1e2cb41d5bdcccd 57 | 328660ef43f66abe2653fa178452a5dfc594c2a1 58 | 8c2a6071f89c90c4dab5ab295d7729d1b54ea60f 59 | b14ed3146f5b2c9bde1703deae9ef33af8110210 60 | cdf8e5c7503a9d22642e3ecfc87817672787b9c5 61 | 68133e19b2dfb9034edf9830a200cfdf38c90cbd 62 | e26646db84b0602f32b34b5a62ca3cae1f91b779 63 | ef58afb697b094423ce90721fbb19a359ef7c50e 64 | 3ee4133d991f52fdf6a25c9834e0745ac74248a4 65 | 52e763a7ddc1aa4fa811578c491c1bc7fd570137 66 | 20d45a6a762535700ce9e0b216e31994335db8a5 67 | 739437bb3dd6d1983e66629c5f08c70e52769371 68 | e0b8a2baee1b77fc703455f39d51477451fc8cfc 69 | 61eb8a50c86b0584bb727dd65bed8d2400d6d5aa 70 | 5db8cda53a6a002db10365967d7f85d19e171b10 71 | f6f5431d25bbf7b12e8add9af5e3475c44a0a5b8 72 | bf7413e8df4e7a34ce9dc13e2f2648783ec54adb 73 | 105b7f253f0ebd7843adaebbd805c944bfb863e4 74 | 9f1adb20baeacc38b3f49f3df6906a0e48f2df3d 75 | badf8b0d34289e679ec65c6c61d3a974353be5cf 76 | 86f9fea5cdecf033161dd2f8f8560768ae0a6d14 77 | 783c138ac81f6a52398564bb17455576e8525b29 78 | 35003c3ef8759c92092f8488fca59a042859018c 79 | 67671d5490c272e3ab7ddd34030d587738df33da 80 | 6fe5a36eef0684af0b91f3b6cfc972d68c4f6fab 81 | 351e605fac813965951ba433b7c2956bf8ad95ce 82 | 20d28d4e87543947c7e4913bcdceaa16e2f8f061 83 | 24cef184714bbd030833904f5265c9c3e12a95a2 84 | 7c99ce73e19f9fbfcce4825ae88261e2b0b0b040 85 | cd03c1e6268ce9b89e3c3eeab8d0f1b6e8cac281 86 | c60111ed3d63b49665747b0e31eb382da5193535 87 | fbc708d671c03e26661b9c08f77598a529858b5e 88 | 38a968fdfb457654c51bcfc4f9174d6ee487bb41 89 | 5c3862203d1e44ab3af441503e22db97b1c5097e 90 | d06b6e206691295ec345782d7ea0686969d8674b 91 | 9978f61b92d16c5f1a463a0995df70da1f7a7d2a 92 | 6534b31208fe6e100d29f9c9c75aac8bf06fbb38 93 | 463013cd41279f2fd0c31d0a16db3972bfffac8d 94 | c6927a00970d0165327d0a6db7950f05720c295c 95 | 5ed822125365274262191d2b77e88d436dd56d88 96 | 2da63cbd251d23c7b633cb287c09e6cf888b3fe4 97 | 578d94dc6f40fff35f91f6fba9b71c46b361dff2 98 | 7eefddd979a1d6bb6f29757a1f463579770ba566 99 | c01bf430a97cbcdaedddba87ef4ea21c456cebdb 100 | c7a7b23f6bd98b8aaf527beb724dda9460b1bc6e 101 | 7c1a77205c03b9909663b2034faa0b544e6bc96b 102 | f72b812932f6d7102233971d65cec0a22b89e136 103 | 695fd6dcf33f47166b25de968b2932b351b0afc4 104 | 93022af9a38f3ebb0c3f15dd1c83f8fadaf64e74 105 | 7c957db6fdd0733bb83bc6d6d747711263ba50b0 106 | 505aaa63a5e209dfb90cee683a8e227a8c278e47 107 | 2e644e46b042ffa86da35c54d7275f1abe6d4911 108 | b166c44f12c7fc565f37ff6288ee64e0f0ec9a0b 109 | aeb0a0197442d4ade8ef41442d557b0e22b85ac0 110 | 0e5f3c406397442996825fd395543514fd06f207 111 | 4cfc43fe12a330c8164251e38c0c0c3c84cf86f6 112 | 4e81efec43c5195aeca0e3877664330418b8e48e 113 | ed673389e4b12925316f9166d56d701829e53cf8 114 | 42773005f9594cd16b10985d428418acb7f352ec 115 | ea0f2b7576bd098921fce9bfebe37f6383e639a4 116 | e3f381c34a20da049779b44cae0417c7fb2898d0 117 | c97f9591e28687be1c4d972e25be7c372a3221b4 118 | f4a4e1c11a5bbbd2fc139d221825407c66e0b8b4 119 | ae6804b35c82f47f8b0a42d8c5e514fe5ef0a883 120 | 4b46e10a541aeec6be3fac709c256fb7da69308e 121 | a6e4818537e42f7b3f021daa810367dad4dda16f 122 | e263b62ea294b9650615a13b926e75944c823990 123 | 7fa4515066ba6905f894b2078f9af7b1379169cf 124 | 75f74467ce7214f1767406d5ed12012aa523c48e 125 | f7079256aa027dc437cbb539f955472416725fc8 126 | 683ea8a1ef06eada90556017d44323b5c04e00f1 127 | a58708aa98ad35c889bb36d8049bf9e9cacfd02a 128 | e170ef514689d7230da362a0c121a07723550512 129 | ba4c2748360a6b66263e11d1dc8658463ca5ff18 130 | a24922852051a9002ebf4c864a55acb75bb4cf75 131 | 41b4b36a6c036568972380177eca2916cacd71de 132 | cecd3ca4319651bd3afd1e23ab66e111ed38d16d 133 | 014e15e4ea6da460cc7835e262676baa37988e4f 134 | 17a5ebfaf62e73f149e33ba674836801f13a80b9 135 | 3b6f58a75a54bfd85d1bc6c51180fdc732992326 136 | 05257be4b57ee43fc09762d5d3a9ad4a6e1a0364 137 | 3482f8986e13c018692053a784481c63a3554c9c 138 | 692a8e583866fc9056f5c61a45969fb9d868a08c 139 | a45dae9cd5d3fde21e5aa9a95367d107267b3b8a 140 | ffbb35a7bb9bbe16c1aa2534f7ff11d59c8e3d1a 141 | 7af50f73fd580f1713af3a6f9c5de49643ec6fc6 142 | 2fcea55e6d027a2ba7c7ebe95eedf47766730fe2 143 | 19ed3e03d19ddcedd5fa86543be820b3a7951650 144 | ed87120066e244ff5331d5f8625873d7a3acc39c 145 | 5abf369388deb8072741b4eb43ef10fa9388a729 146 | dca7ebfb78ce21884300f133d89244bc4b1b756f 147 | 5318b9d7fcc93873f768725eb68ba2c924bb07ee 148 | a3e3612e586fd206efb8eee6ccd58318e182829a 149 | 7e827e3b90da24c2a15f7b67e3bbece39955a5d0 150 | e08c4d3bc9cf2b3e2cb88de2bfaa4fe8c7aa3f24 151 | 1a4fb632f0de0c53a0a31d57f840a19e56c645ee 152 | da56cd815fa2f0d6a4ce6d25ed7b1a01d9f9bc6b 153 | 4ccf94a1b0efd63cddeee0ef5eee5ebe720cfcbf 154 | edd2e206825fa8949d1304cd82c08d64b222f2eb 155 | 6b8b7830f73c5bf9e8beb9f161ad82b3bde992e4 156 | 9ea3f29aaedf7da10b1488934c50a39e271b0b64 157 | 242d790e5a168043c76f0539fd894b73ee67b3b3 158 | 628dacebb0faa7f81670e174ca4c8a95a7e37029 159 | 2ac1295b4e54b3f15bb0a99f84018d2082495645 160 | e84818e1bf7f699aa6e28ef9edfb582099099292 161 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # ecloop 2 | 3 | A high-performance, CPU-optimized tool for computing public keys on the secp256k1 elliptic curve. It includes features for searching both compressed and uncompressed public keys, with customizable search parameters. Discuss on [bitcointalk.org](https://bitcointalk.org/index.php?topic=5544649.0). 4 | 5 | [donate](https://buymeacoffee.com/vladkens) 6 | 7 | ## Features 8 | 9 | - 🍏 Fixed 256-bit modular arithmetic 10 | - 🔄 Group inversion for point addition operations 11 | - 🍇 Precomputed tables for point multiplication 12 | - 🔍 Search for compressed and uncompressed public keys (hash160) 13 | - 🌟 Accelerated SHA-256 with SHA extension (both ARM and x86) 14 | - 🚀 Accelerated RIPEMD-160 [using SIMD](https://vladkens.cc/rmd160-simd/) (AVX2/NEON) 15 | - 🎲 Random search within customizable bit ranges 16 | - 🍎 Works seamlessly on macOS and Linux 17 | - 🔧 Customizable search range and thread count for flexible usage 18 | 19 | ## Build 20 | 21 | ```sh 22 | git clone https://github.com/vladkens/ecloop.git && cd ecloop 23 | make build 24 | ``` 25 | 26 | _\* On macOS, you may need to run `xcode-select --install` first._ 27 | 28 | By default, `cc` is used as the compiler. Using `clang` may produce [faster code](https://github.com/vladkens/ecloop/issues/7) than `gcc`. You can explicitly specify the compiler for any `make` command using the `CC` parameter. For example: `make add CC=clang`. 29 | 30 | Also, verify correctness with the following commands (some compiler versions may have issues with built-ins used in the code): 31 | 32 | ```sh 33 | make add # should found 9 keys 34 | make mul # should found 1080 keys 35 | ``` 36 | 37 | ## Usage 38 | 39 | ```text 40 | Usage: ./ecloop [-t ] [-f ] [-a ] [-r ] 41 | 42 | Compute commands: 43 | add - search in given range with batch addition 44 | mul - search hex encoded private keys (from stdin) 45 | rnd - search random range of bits in given range 46 | 47 | Compute options: 48 | -f - filter file to search (list of hashes or bloom fitler) 49 | -o - output file to write found keys (default: stdout) 50 | -t - number of threads to run (default: 1) 51 | -a - address type to search: c - addr33, u - addr65 (default: c) 52 | -r - search range in hex format (example: 8000:ffff, default all) 53 | -q - quiet mode (no output to stdout; -o required) 54 | -endo - use endomorphism (default: false) 55 | 56 | Other commands: 57 | blf-gen - create bloom filter from list of hex-encoded hash160 58 | bench - run benchmark of internal functions 59 | bench-gtable - run benchmark of ecc multiplication (with different table size) 60 | ``` 61 | 62 | ### Quick Start for Bitcoin Puzzles 63 | 64 | For a quick start with Bitcoin Puzzles, there are preconfigured make commands. Simply run them, and `ecloop` will start searching for the puzzle in random mode. For example, use `make 71` for puzzle 71, `make 72` for puzzle 72, and so on. If you are lucky and find a key, the result will be saved in the `found_N.txt` file. Shortcuts available for: `28`, `32`, `36`, `71`, `73`, `74`, `75`, `76`, `77`, `78`, `79`. 65 | 66 | ### Check keys in a given range (sequential addition) 67 | 68 | ```sh 69 | ./ecloop add -f data/btc-puzzles-hash -t 4 -r 800000:ffffff -o /tmp/found.txt 70 | ``` 71 | 72 | - `-f` is a filter file with hash160 values to search for. It can be a list of hex-encoded hashes (one per line) or a Bloom filter (must have a `.blf` extension). 73 | - `-t` sets the number of threads (e.g., 4). 74 | - `r` defines the start:end of the search range. 75 | - `-o` specifies the file where found keys will be saved (if not provided, `stdout` will be used). 76 | - No `-a` option is provided, so only `c` (compressed) hash160 values will be checked. 77 | 78 | ### Check a given list of keys (multiplication) 79 | 80 | ```sh 81 | cat privkeys.txt | ./ecloop mul -f data/btc-puzzles.blf -a cu -t 4 82 | ``` 83 | 84 | - `cat privkeys.txt` – the source of HEX-encoded private keys to search (can be a file or a generator program). 85 | - `-f` – a Bloom filter containing hash160 values to search for (may produce false positives but has a much smaller size; for example, all BTC addresses ever used take up ~6 GB). 86 | - `-a` – specifies which type of hash160 to search: `c` for compressed, `u` for uncompressed, `cu` to check both. 87 | - `-t` – sets the number of threads (e.g., 4). 88 | 89 | `ecloop` can also take a raw word list and automatically hash each word with SHA-256. Use the `-raw` flag to enable this: 90 | 91 | ```sh 92 | cat wordlist.txt | ./ecloop mul -f data/btc-puzzles.blf -a cu -t 4 -raw 93 | ``` 94 | 95 | ### Random Search 96 | 97 | The `rnd` command allows you to search random bit ranges within a specified range (by default, the entire curve space). This mode is useful for exploring random subsets of the keyspace. 98 | 99 | #### Example 1: Random Search with Default Offset and Size 100 | 101 | ```sh 102 | ./ecloop rnd -f data/btc-puzzles-hash -o ./found.txt -a cu -q 103 | ``` 104 | 105 | This command performs a random search across the entire keyspace, checking both compressed and uncompressed addresses (`-a cu`), and saves the results to `found.txt`. Quiet mode is enabled (`-q`). 106 | 107 | A random bit offset will be used, with a 32-bit range per iteration (4.2M keys). 108 | 109 | #### Example 2: Random Search with Custom Offset and Size 110 | 111 | ```sh 112 | ./ecloop rnd -f data/btc-puzzles-hash -d 128:32 113 | ``` 114 | 115 | This command searches a 32-bit range with a 128-bit offset (`-d 128:32`). It will execute a search with a random base key on each large iteration. Bits from `offset` to `offset + size` will be dynamic. For example: 116 | 117 | ```txt 118 | iter1: d33abfe4b9152c08 7176d067XXXXXXXX 27d4419e6969a205 4d1deb10e4929621 119 | iter2: 1b354d3094405c2f bf8f5c15XXXXXXXX 46804248255476e9 800f26edd71ad0d7 120 | ``` 121 | 122 | X represents dynamic bits; other bits are randomly generated during large iterations. 123 | 124 | _Note: You can also combine random search with `-r` param for shorter ranges._ 125 | 126 | ### Generating bloom filter 127 | 128 | ```sh 129 | cat data/btc-puzzles-hash | ./ecloop blf-gen -n 1024 -o /tmp/test.blf 130 | ``` 131 | 132 | - `cat` reads the list of hex-encoded hash160 values from a file. 133 | - `-n` specifies the number of entries for the Bloom filter (the number of hashes). 134 | - `-o` defines the output file where the filter will be written (the `.blf` extension is required). 135 | 136 | The Bloom filter uses p = 0.000001 (1 in 1,000,000 false positives). You can adjust this option by modifying `n`. See the [Bloom Filter Calculator](https://hur.st/bloomfilter/?n=1024&p=0.000001&m=&k=20). 137 | 138 | A list of all addresses can be found [here](https://bitcointalk.org/index.php?topic=5265993.0) or use [`bcloop`](https://github.com/vladkens/bcloop) to make dump from Bitcoin Node. 139 | 140 | Created Bloom filter then can be used in `ecloop` as a filter: 141 | 142 | ```sh 143 | ./ecloop add -f /tmp/test.blf -t 4 -r 8000:ffffff 144 | ``` 145 | 146 | _Note: Bloom filter works with all search commands (`add`, `mul`, `rnd`)._ 147 | 148 | ## Benchmark 149 | 150 | Get the performance of different functions for a single thread: 151 | 152 | ```sh 153 | ./ecloop bench 154 | ``` 155 | 156 | Should print output like this: 157 | 158 | ```sh 159 | _ec_jacobi_add1: 6.70M it/s ~ 0.90s 160 | _ec_jacobi_add2: 5.44M it/s ~ 1.10s 161 | _ec_jacobi_dbl1: 5.47M it/s ~ 1.10s 162 | _ec_jacobi_dbl2: 7.81M it/s ~ 0.77s 163 | ec_jacobi_mul: 0.02M it/s ~ 0.55s 164 | ec_gtable_mul: 0.32M it/s ~ 1.54s 165 | ec_affine_add: 0.31M it/s ~ 1.63s 166 | ec_affine_dbl: 0.31M it/s ~ 1.63s 167 | _fe_modinv_binpow: 0.20M it/s ~ 0.50s 168 | _fe_modinv_addchn: 0.31M it/s ~ 0.32s 169 | addr33: 4.84M it/s ~ 1.03s 170 | addr65: 4.27M it/s ~ 1.17s 171 | ``` 172 | 173 | _Note: This benchmark is run on a MacBook Pro M2._ 174 | 175 | ## Build on Windows with WSL 176 | 177 | Here are the steps I followed to run `ecloop` on Windows: 178 | 179 | 1. Open PowerShell 180 | 2. Run `wsl --install` 181 | 3. Restart Windows. 182 | 4. Run `wsl --install Ubuntu` (this command hung when I tried it, so I continued in a new tab) 183 | 5. Run `wsl -d Ubuntu` 184 | 6. Run: `sudo apt update && sudo apt install -y build-essential git clang` 185 | 7. Run `cd ~ && git clone https://github.com/vladkens/ecloop.git && cd ecloop` 186 | 8. Run `make build` 187 | 188 | If no errors appear, `ecloop` has been compiled successfully and is ready to use. For example, you can run a benchmark with: `./ecloop bench`. 189 | 190 | ## Performance compare 191 | 192 | Tests were done on an Intel N100. 193 | 194 | ### Single thread 195 | 196 | ```sh 197 | > time ./keyhunt -m rmd160 -f ../ecloop/data/btc-puzzles-hash -r 8000:fffffff -t 1 -n 16777216 198 | 3m53s ~ 1.15 MKeys/s 199 | 200 | > time ./ecloop add -f data/btc-puzzles-hash -t 1 -r 8000:fffffff 201 | 1m06s ~ 4.09 MKeys/s 202 | ``` 203 | 204 | ### Multiple threads 205 | 206 | ```sh 207 | > time ./keyhunt -m rmd160 -f ../ecloop/data/btc-puzzles-hash -r 8000:fffffff -t 4 -n 16777216 208 | 1m31s ~ 2.95 MKeys/s 209 | 210 | > time ./ecloop add -f data/btc-puzzles-hash -t 4 -r 8000:fffffff 211 | 0m25s ~ 10.73 MKeys/s 212 | ``` 213 | 214 | ## Disclaimer 215 | 216 | This project was created to explore the mathematics behind elliptic curves in cryptocurrencies. The functionality for searching Bitcoin Puzzles was added as a real-world use case. 217 | 218 | ## Donations 219 | 220 | If you find this project useful, consider supporting its development: 221 | 222 | - **BTC**: `bc1q4c3mxpm50awx9qaprx54k5x3t5m9ex658yzk4j` 223 | - **ETH**: `0x4DF8E04C5AC0b06fb9938581D8a1732D5A78703E` 224 | - **SOL**: `4r3CeYxvwJa1btZudmLpHeu2yzeudRw4UTqMUZScD63j` 225 | - **XMR**: `85yjYN1sU3sgGFEkdqLKKxdiQwzQjqyhT74m5j4xwmKqHYfensRMjJrB1HvE9H6R6G5wG938KDpkJLum6GQd5q5yTTk8uhj` 226 | - [Buy Me a Coffee](https://buymeacoffee.com/vladkens) 227 | 228 | Thank you for your support! 229 | 230 | ## Cudos to 231 | 232 | - [sharpden](https://github.com/sharpden) 233 | 234 | ## See also 235 | 236 | - [ryancdotorg/brainflayer](https://github.com/ryancdotorg/brainflayer) 237 | - [albertobsd/keyhunt](https://github.com/albertobsd/keyhunt) 238 | - [JeanLucPons/VanitySearch](https://github.com/JeanLucPons/VanitySearch) 239 | -------------------------------------------------------------------------------- /lib/rmd160s.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) vladkens 2 | // https://github.com/vladkens/ecloop 3 | // Licensed under the MIT License. 4 | 5 | #pragma once 6 | #include 7 | #include 8 | 9 | // https://homes.esat.kuleuven.be/~bosselae/ripemd160/pdf/AB-9601/AB-9601.pdf 10 | // https://homes.esat.kuleuven.be/~bosselae/ripemd160/ps/AB-9601/rmd160.h 11 | // https://homes.esat.kuleuven.be/~bosselae/ripemd160/ps/AB-9601/rmd160.c 12 | 13 | #define RMD_K1 0x67452301 14 | #define RMD_K2 0xEFCDAB89 15 | #define RMD_K3 0x98BADCFE 16 | #define RMD_K4 0x10325476 17 | #define RMD_K5 0xC3D2E1F0 18 | 19 | #if defined(__aarch64__) && defined(__ARM_NEON) && !defined(NO_SIMD) 20 | #include 21 | 22 | #define RMD_LEN 4 23 | #define RMD_VEC uint32x4_t 24 | #define RMD_LD_NUM(x) vdupq_n_u32(x) 25 | 26 | #define RMD_SWAP(x) vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(x))) 27 | #define RMD_LOAD(x, i) vld1q_u32(((uint32_t[4]){x[0][i], x[1][i], x[2][i], x[3][i]})) 28 | #define RMD_DUMP(r, s, i) \ 29 | do { \ 30 | alignas(16) uint32_t tmp[4]; \ 31 | vst1q_u32(tmp, s[i]); \ 32 | for (int j = 0; j < 4; ++j) r[j][i] = tmp[j]; \ 33 | } while (0); 34 | 35 | #define RMD_F1(x, y, z) veorq_u32(veorq_u32(x, y), z) 36 | #define RMD_F2(x, y, z) vbslq_u32(x, y, z) 37 | #define RMD_F3(x, y, z) veorq_u32(vorrq_u32(x, vmvnq_u32(y)), z) 38 | #define RMD_F4(x, y, z) vbslq_u32(z, x, y) 39 | #define RMD_F5(x, y, z) veorq_u32(x, vorrq_u32(y, vmvnq_u32(z))) 40 | 41 | #define RMD_ROTL(x, n) vsriq_n_u32(vshlq_n_u32(x, n), x, 32 - (n)) 42 | #define RMD_ADD2(a, b) vaddq_u32(a, b) 43 | #define RMD_ADD3(a, b, c) vaddq_u32(vaddq_u32(a, b), c) 44 | #define RMD_ADD4(a, b, c, d) vaddq_u32(vaddq_u32(vaddq_u32(a, b), c), d) 45 | 46 | #elif defined(__x86_64__) && defined(__AVX2__) && !defined(NO_SIMD) 47 | #include 48 | 49 | #define RMD_LEN 8 50 | #define RMD_VEC __m256i 51 | #define RMD_LD_NUM(x) _mm256_set1_epi32(x) 52 | 53 | #define RMD_SWAP(x) \ 54 | _mm256_shuffle_epi8((x), _mm256_setr_epi8(3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, \ 55 | 12, 19, 18, 17, 16, 23, 22, 21, 20, 27, 26, 25, 24, \ 56 | 31, 30, 29, 28)) 57 | 58 | #define RMD_LOAD(x, i) \ 59 | _mm256_set_epi32(x[7][i], x[6][i], x[5][i], x[4][i], x[3][i], x[2][i], x[1][i], x[0][i]) 60 | 61 | #define RMD_DUMP(r, s, i) \ 62 | do { \ 63 | alignas(32) int32_t tmp[8]; \ 64 | _mm256_store_si256((__m256i *)tmp, s[i]); \ 65 | for (int j = 0; j < 8; ++j) r[j][i] = tmp[j]; \ 66 | } while (0); 67 | 68 | #define _mm256_not_si256(x) _mm256_xor_si256((x), _mm256_set1_epi32(0xffffffff)) 69 | #define RMD_F1(x, y, z) _mm256_xor_si256(x, _mm256_xor_si256(y, z)) 70 | #define RMD_F2(x, y, z) _mm256_or_si256(_mm256_and_si256(x, y), _mm256_andnot_si256(x, z)) 71 | #define RMD_F3(x, y, z) _mm256_xor_si256(_mm256_or_si256(x, _mm256_not_si256(y)), z) 72 | #define RMD_F4(x, y, z) _mm256_or_si256(_mm256_and_si256(x, z), _mm256_andnot_si256(z, y)) 73 | #define RMD_F5(x, y, z) _mm256_xor_si256(x, _mm256_or_si256(y, _mm256_not_si256(z))) 74 | 75 | #define RMD_ROTL(x, n) _mm256_or_si256(_mm256_slli_epi32(x, n), _mm256_srli_epi32(x, 32 - (n))) 76 | #define RMD_ADD2(a, b) _mm256_add_epi32(a, b) 77 | #define RMD_ADD3(a, b, c) _mm256_add_epi32(_mm256_add_epi32(a, b), c) 78 | #define RMD_ADD4(a, b, c, d) _mm256_add_epi32(_mm256_add_epi32(a, b), _mm256_add_epi32(c, d)) 79 | 80 | #else 81 | #warning "Fallback RIPEMD-160 implementation used. AVX2 or NEON required for SIMD" 82 | 83 | #define RMD_LEN 1 84 | #define RMD_VEC uint32_t 85 | #define RMD_LD_NUM(x) x 86 | 87 | #define RMD_SWAP(x) __builtin_bswap32(x) 88 | #define RMD_LOAD(x, i) x[0][i] 89 | #define RMD_DUMP(r, s, i) r[0][i] = s[i] 90 | 91 | #define RMD_F1(x, y, z) ((x) ^ (y) ^ (z)) 92 | #define RMD_F2(x, y, z) (((x) & (y)) | (~(x) & (z))) 93 | #define RMD_F3(x, y, z) (((x) | ~(y)) ^ (z)) 94 | #define RMD_F4(x, y, z) (((x) & (z)) | ((y) & ~(z))) 95 | #define RMD_F5(x, y, z) ((x) ^ ((y) | ~(z))) 96 | 97 | #define RMD_ROTL(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) 98 | #define RMD_ADD2(a, b) (a + b) 99 | #define RMD_ADD3(a, b, c) (a + b + c) 100 | #define RMD_ADD4(a, b, c, d) (a + b + c + d) 101 | 102 | #endif 103 | 104 | #define RMD_RN(a, b, c, d, e, f, x, k, r) \ 105 | u = RMD_ADD4(a, f, x, RMD_LD_NUM(k)); \ 106 | a = RMD_ADD2(RMD_ROTL(u, r), e); \ 107 | c = RMD_ROTL(c, 10); 108 | 109 | #define RMD_L1(a, b, c, d, e, x, r) RMD_RN(a, b, c, d, e, RMD_F1(b, c, d), x, 0, r) 110 | #define RMD_L2(a, b, c, d, e, x, r) RMD_RN(a, b, c, d, e, RMD_F2(b, c, d), x, 0x5A827999ul, r) 111 | #define RMD_L3(a, b, c, d, e, x, r) RMD_RN(a, b, c, d, e, RMD_F3(b, c, d), x, 0x6ED9EBA1ul, r) 112 | #define RMD_L4(a, b, c, d, e, x, r) RMD_RN(a, b, c, d, e, RMD_F4(b, c, d), x, 0x8F1BBCDCul, r) 113 | #define RMD_L5(a, b, c, d, e, x, r) RMD_RN(a, b, c, d, e, RMD_F5(b, c, d), x, 0xA953FD4Eul, r) 114 | #define RMD_R1(a, b, c, d, e, x, r) RMD_RN(a, b, c, d, e, RMD_F5(b, c, d), x, 0x50A28BE6ul, r) 115 | #define RMD_R2(a, b, c, d, e, x, r) RMD_RN(a, b, c, d, e, RMD_F4(b, c, d), x, 0x5C4DD124ul, r) 116 | #define RMD_R3(a, b, c, d, e, x, r) RMD_RN(a, b, c, d, e, RMD_F3(b, c, d), x, 0x6D703EF3ul, r) 117 | #define RMD_R4(a, b, c, d, e, x, r) RMD_RN(a, b, c, d, e, RMD_F2(b, c, d), x, 0x7A6D76E9ul, r) 118 | #define RMD_R5(a, b, c, d, e, x, r) RMD_RN(a, b, c, d, e, RMD_F1(b, c, d), x, 0, r) 119 | 120 | #define RMD_LOAD_SWAP(x, i) RMD_SWAP(RMD_LOAD(x, i)) 121 | 122 | void rmd160_block(RMD_VEC *s, const uint32_t x[RMD_LEN][16]) { 123 | RMD_VEC a1, b1, c1, d1, e1, a2, b2, c2, d2, e2, u; 124 | a1 = a2 = RMD_LD_NUM(RMD_K1); 125 | b1 = b2 = RMD_LD_NUM(RMD_K2); 126 | c1 = c2 = RMD_LD_NUM(RMD_K3); 127 | d1 = d2 = RMD_LD_NUM(RMD_K4); 128 | e1 = e2 = RMD_LD_NUM(RMD_K5); 129 | 130 | RMD_VEC w[16]; 131 | // for (int i = 0; i < 16; i++) w[i] = RMD_LOAD(x, i); 132 | 133 | // SHA256 is big-endian, but RIPEMD-160 is little-endian, so swap bytes here 134 | // keep unrolled let ILP decide how to schedule 135 | w[0] = RMD_LOAD_SWAP(x, 0); 136 | w[1] = RMD_LOAD_SWAP(x, 1); 137 | w[2] = RMD_LOAD_SWAP(x, 2); 138 | w[3] = RMD_LOAD_SWAP(x, 3); 139 | w[4] = RMD_LOAD_SWAP(x, 4); 140 | w[5] = RMD_LOAD_SWAP(x, 5); 141 | w[6] = RMD_LOAD_SWAP(x, 6); 142 | w[7] = RMD_LOAD_SWAP(x, 7); 143 | w[8] = RMD_LOAD_SWAP(x, 8); 144 | w[9] = RMD_LOAD_SWAP(x, 9); 145 | w[10] = RMD_LOAD_SWAP(x, 10); 146 | w[11] = RMD_LOAD_SWAP(x, 11); 147 | w[12] = RMD_LOAD_SWAP(x, 12); 148 | w[13] = RMD_LOAD_SWAP(x, 13); 149 | w[14] = RMD_LOAD_SWAP(x, 14); 150 | w[15] = RMD_LOAD_SWAP(x, 15); 151 | 152 | RMD_L1(a1, b1, c1, d1, e1, w[0], 11); 153 | RMD_R1(a2, b2, c2, d2, e2, w[5], 8); 154 | RMD_L1(e1, a1, b1, c1, d1, w[1], 14); 155 | RMD_R1(e2, a2, b2, c2, d2, w[14], 9); 156 | RMD_L1(d1, e1, a1, b1, c1, w[2], 15); 157 | RMD_R1(d2, e2, a2, b2, c2, w[7], 9); 158 | RMD_L1(c1, d1, e1, a1, b1, w[3], 12); 159 | RMD_R1(c2, d2, e2, a2, b2, w[0], 11); 160 | RMD_L1(b1, c1, d1, e1, a1, w[4], 5); 161 | RMD_R1(b2, c2, d2, e2, a2, w[9], 13); 162 | RMD_L1(a1, b1, c1, d1, e1, w[5], 8); 163 | RMD_R1(a2, b2, c2, d2, e2, w[2], 15); 164 | RMD_L1(e1, a1, b1, c1, d1, w[6], 7); 165 | RMD_R1(e2, a2, b2, c2, d2, w[11], 15); 166 | RMD_L1(d1, e1, a1, b1, c1, w[7], 9); 167 | RMD_R1(d2, e2, a2, b2, c2, w[4], 5); 168 | RMD_L1(c1, d1, e1, a1, b1, w[8], 11); 169 | RMD_R1(c2, d2, e2, a2, b2, w[13], 7); 170 | RMD_L1(b1, c1, d1, e1, a1, w[9], 13); 171 | RMD_R1(b2, c2, d2, e2, a2, w[6], 7); 172 | RMD_L1(a1, b1, c1, d1, e1, w[10], 14); 173 | RMD_R1(a2, b2, c2, d2, e2, w[15], 8); 174 | RMD_L1(e1, a1, b1, c1, d1, w[11], 15); 175 | RMD_R1(e2, a2, b2, c2, d2, w[8], 11); 176 | RMD_L1(d1, e1, a1, b1, c1, w[12], 6); 177 | RMD_R1(d2, e2, a2, b2, c2, w[1], 14); 178 | RMD_L1(c1, d1, e1, a1, b1, w[13], 7); 179 | RMD_R1(c2, d2, e2, a2, b2, w[10], 14); 180 | RMD_L1(b1, c1, d1, e1, a1, w[14], 9); 181 | RMD_R1(b2, c2, d2, e2, a2, w[3], 12); 182 | RMD_L1(a1, b1, c1, d1, e1, w[15], 8); 183 | RMD_R1(a2, b2, c2, d2, e2, w[12], 6); 184 | 185 | RMD_L2(e1, a1, b1, c1, d1, w[7], 7); 186 | RMD_R2(e2, a2, b2, c2, d2, w[6], 9); 187 | RMD_L2(d1, e1, a1, b1, c1, w[4], 6); 188 | RMD_R2(d2, e2, a2, b2, c2, w[11], 13); 189 | RMD_L2(c1, d1, e1, a1, b1, w[13], 8); 190 | RMD_R2(c2, d2, e2, a2, b2, w[3], 15); 191 | RMD_L2(b1, c1, d1, e1, a1, w[1], 13); 192 | RMD_R2(b2, c2, d2, e2, a2, w[7], 7); 193 | RMD_L2(a1, b1, c1, d1, e1, w[10], 11); 194 | RMD_R2(a2, b2, c2, d2, e2, w[0], 12); 195 | RMD_L2(e1, a1, b1, c1, d1, w[6], 9); 196 | RMD_R2(e2, a2, b2, c2, d2, w[13], 8); 197 | RMD_L2(d1, e1, a1, b1, c1, w[15], 7); 198 | RMD_R2(d2, e2, a2, b2, c2, w[5], 9); 199 | RMD_L2(c1, d1, e1, a1, b1, w[3], 15); 200 | RMD_R2(c2, d2, e2, a2, b2, w[10], 11); 201 | RMD_L2(b1, c1, d1, e1, a1, w[12], 7); 202 | RMD_R2(b2, c2, d2, e2, a2, w[14], 7); 203 | RMD_L2(a1, b1, c1, d1, e1, w[0], 12); 204 | RMD_R2(a2, b2, c2, d2, e2, w[15], 7); 205 | RMD_L2(e1, a1, b1, c1, d1, w[9], 15); 206 | RMD_R2(e2, a2, b2, c2, d2, w[8], 12); 207 | RMD_L2(d1, e1, a1, b1, c1, w[5], 9); 208 | RMD_R2(d2, e2, a2, b2, c2, w[12], 7); 209 | RMD_L2(c1, d1, e1, a1, b1, w[2], 11); 210 | RMD_R2(c2, d2, e2, a2, b2, w[4], 6); 211 | RMD_L2(b1, c1, d1, e1, a1, w[14], 7); 212 | RMD_R2(b2, c2, d2, e2, a2, w[9], 15); 213 | RMD_L2(a1, b1, c1, d1, e1, w[11], 13); 214 | RMD_R2(a2, b2, c2, d2, e2, w[1], 13); 215 | RMD_L2(e1, a1, b1, c1, d1, w[8], 12); 216 | RMD_R2(e2, a2, b2, c2, d2, w[2], 11); 217 | 218 | RMD_L3(d1, e1, a1, b1, c1, w[3], 11); 219 | RMD_R3(d2, e2, a2, b2, c2, w[15], 9); 220 | RMD_L3(c1, d1, e1, a1, b1, w[10], 13); 221 | RMD_R3(c2, d2, e2, a2, b2, w[5], 7); 222 | RMD_L3(b1, c1, d1, e1, a1, w[14], 6); 223 | RMD_R3(b2, c2, d2, e2, a2, w[1], 15); 224 | RMD_L3(a1, b1, c1, d1, e1, w[4], 7); 225 | RMD_R3(a2, b2, c2, d2, e2, w[3], 11); 226 | RMD_L3(e1, a1, b1, c1, d1, w[9], 14); 227 | RMD_R3(e2, a2, b2, c2, d2, w[7], 8); 228 | RMD_L3(d1, e1, a1, b1, c1, w[15], 9); 229 | RMD_R3(d2, e2, a2, b2, c2, w[14], 6); 230 | RMD_L3(c1, d1, e1, a1, b1, w[8], 13); 231 | RMD_R3(c2, d2, e2, a2, b2, w[6], 6); 232 | RMD_L3(b1, c1, d1, e1, a1, w[1], 15); 233 | RMD_R3(b2, c2, d2, e2, a2, w[9], 14); 234 | RMD_L3(a1, b1, c1, d1, e1, w[2], 14); 235 | RMD_R3(a2, b2, c2, d2, e2, w[11], 12); 236 | RMD_L3(e1, a1, b1, c1, d1, w[7], 8); 237 | RMD_R3(e2, a2, b2, c2, d2, w[8], 13); 238 | RMD_L3(d1, e1, a1, b1, c1, w[0], 13); 239 | RMD_R3(d2, e2, a2, b2, c2, w[12], 5); 240 | RMD_L3(c1, d1, e1, a1, b1, w[6], 6); 241 | RMD_R3(c2, d2, e2, a2, b2, w[2], 14); 242 | RMD_L3(b1, c1, d1, e1, a1, w[13], 5); 243 | RMD_R3(b2, c2, d2, e2, a2, w[10], 13); 244 | RMD_L3(a1, b1, c1, d1, e1, w[11], 12); 245 | RMD_R3(a2, b2, c2, d2, e2, w[0], 13); 246 | RMD_L3(e1, a1, b1, c1, d1, w[5], 7); 247 | RMD_R3(e2, a2, b2, c2, d2, w[4], 7); 248 | RMD_L3(d1, e1, a1, b1, c1, w[12], 5); 249 | RMD_R3(d2, e2, a2, b2, c2, w[13], 5); 250 | 251 | RMD_L4(c1, d1, e1, a1, b1, w[1], 11); 252 | RMD_R4(c2, d2, e2, a2, b2, w[8], 15); 253 | RMD_L4(b1, c1, d1, e1, a1, w[9], 12); 254 | RMD_R4(b2, c2, d2, e2, a2, w[6], 5); 255 | RMD_L4(a1, b1, c1, d1, e1, w[11], 14); 256 | RMD_R4(a2, b2, c2, d2, e2, w[4], 8); 257 | RMD_L4(e1, a1, b1, c1, d1, w[10], 15); 258 | RMD_R4(e2, a2, b2, c2, d2, w[1], 11); 259 | RMD_L4(d1, e1, a1, b1, c1, w[0], 14); 260 | RMD_R4(d2, e2, a2, b2, c2, w[3], 14); 261 | RMD_L4(c1, d1, e1, a1, b1, w[8], 15); 262 | RMD_R4(c2, d2, e2, a2, b2, w[11], 14); 263 | RMD_L4(b1, c1, d1, e1, a1, w[12], 9); 264 | RMD_R4(b2, c2, d2, e2, a2, w[15], 6); 265 | RMD_L4(a1, b1, c1, d1, e1, w[4], 8); 266 | RMD_R4(a2, b2, c2, d2, e2, w[0], 14); 267 | RMD_L4(e1, a1, b1, c1, d1, w[13], 9); 268 | RMD_R4(e2, a2, b2, c2, d2, w[5], 6); 269 | RMD_L4(d1, e1, a1, b1, c1, w[3], 14); 270 | RMD_R4(d2, e2, a2, b2, c2, w[12], 9); 271 | RMD_L4(c1, d1, e1, a1, b1, w[7], 5); 272 | RMD_R4(c2, d2, e2, a2, b2, w[2], 12); 273 | RMD_L4(b1, c1, d1, e1, a1, w[15], 6); 274 | RMD_R4(b2, c2, d2, e2, a2, w[13], 9); 275 | RMD_L4(a1, b1, c1, d1, e1, w[14], 8); 276 | RMD_R4(a2, b2, c2, d2, e2, w[9], 12); 277 | RMD_L4(e1, a1, b1, c1, d1, w[5], 6); 278 | RMD_R4(e2, a2, b2, c2, d2, w[7], 5); 279 | RMD_L4(d1, e1, a1, b1, c1, w[6], 5); 280 | RMD_R4(d2, e2, a2, b2, c2, w[10], 15); 281 | RMD_L4(c1, d1, e1, a1, b1, w[2], 12); 282 | RMD_R4(c2, d2, e2, a2, b2, w[14], 8); 283 | 284 | RMD_L5(b1, c1, d1, e1, a1, w[4], 9); 285 | RMD_R5(b2, c2, d2, e2, a2, w[12], 8); 286 | RMD_L5(a1, b1, c1, d1, e1, w[0], 15); 287 | RMD_R5(a2, b2, c2, d2, e2, w[15], 5); 288 | RMD_L5(e1, a1, b1, c1, d1, w[5], 5); 289 | RMD_R5(e2, a2, b2, c2, d2, w[10], 12); 290 | RMD_L5(d1, e1, a1, b1, c1, w[9], 11); 291 | RMD_R5(d2, e2, a2, b2, c2, w[4], 9); 292 | RMD_L5(c1, d1, e1, a1, b1, w[7], 6); 293 | RMD_R5(c2, d2, e2, a2, b2, w[1], 12); 294 | RMD_L5(b1, c1, d1, e1, a1, w[12], 8); 295 | RMD_R5(b2, c2, d2, e2, a2, w[5], 5); 296 | RMD_L5(a1, b1, c1, d1, e1, w[2], 13); 297 | RMD_R5(a2, b2, c2, d2, e2, w[8], 14); 298 | RMD_L5(e1, a1, b1, c1, d1, w[10], 12); 299 | RMD_R5(e2, a2, b2, c2, d2, w[7], 6); 300 | RMD_L5(d1, e1, a1, b1, c1, w[14], 5); 301 | RMD_R5(d2, e2, a2, b2, c2, w[6], 8); 302 | RMD_L5(c1, d1, e1, a1, b1, w[1], 12); 303 | RMD_R5(c2, d2, e2, a2, b2, w[2], 13); 304 | RMD_L5(b1, c1, d1, e1, a1, w[3], 13); 305 | RMD_R5(b2, c2, d2, e2, a2, w[13], 6); 306 | RMD_L5(a1, b1, c1, d1, e1, w[8], 14); 307 | RMD_R5(a2, b2, c2, d2, e2, w[14], 5); 308 | RMD_L5(e1, a1, b1, c1, d1, w[11], 11); 309 | RMD_R5(e2, a2, b2, c2, d2, w[0], 15); 310 | RMD_L5(d1, e1, a1, b1, c1, w[6], 8); 311 | RMD_R5(d2, e2, a2, b2, c2, w[3], 13); 312 | RMD_L5(c1, d1, e1, a1, b1, w[15], 5); 313 | RMD_R5(c2, d2, e2, a2, b2, w[9], 11); 314 | RMD_L5(b1, c1, d1, e1, a1, w[13], 6); 315 | RMD_R5(b2, c2, d2, e2, a2, w[11], 11); 316 | 317 | RMD_VEC t = s[0]; 318 | s[0] = RMD_ADD3(s[1], c1, d2); 319 | s[1] = RMD_ADD3(s[2], d1, e2); 320 | s[2] = RMD_ADD3(s[3], e1, a2); 321 | s[3] = RMD_ADD3(s[4], a1, b2); 322 | s[4] = RMD_ADD3(t, b1, c2); 323 | } 324 | 325 | void rmd160_batch(uint32_t r[RMD_LEN][5], const uint32_t x[RMD_LEN][16]) { 326 | RMD_VEC s[5] = {0}; // load initial state 327 | s[0] = RMD_LD_NUM(RMD_K1); 328 | s[1] = RMD_LD_NUM(RMD_K2); 329 | s[2] = RMD_LD_NUM(RMD_K3); 330 | s[3] = RMD_LD_NUM(RMD_K4); 331 | s[4] = RMD_LD_NUM(RMD_K5); 332 | 333 | rmd160_block((RMD_VEC *)s, x); // round 334 | for (int i = 0; i < 5; ++i) s[i] = RMD_SWAP(s[i]); // change endian 335 | for (int i = 0; i < 5; ++i) RMD_DUMP(r, s, i); // dump data to array 336 | } 337 | -------------------------------------------------------------------------------- /lib/utils.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) vladkens 2 | // https://github.com/vladkens/ecloop 3 | // Licensed under the MIT License. 4 | 5 | #pragma once 6 | 7 | #include "ecc.c" 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #ifdef _WIN32 20 | #include 21 | #else 22 | #include 23 | #include 24 | #endif 25 | 26 | typedef char hex40[41]; // rmd160 hex string 27 | typedef char hex64[65]; // sha256 hex string 28 | typedef u32 h160_t[5]; 29 | 30 | // Mark: Terminal 31 | 32 | #define COLOR_YELLOW "\033[33m" 33 | #define COLOR_RESET "\033[0m" 34 | 35 | void term_clear_line() { 36 | fprintf(stderr, "\033[2K\r"); 37 | // in case if ecloop will be piped 38 | fflush(stdout); 39 | fflush(stderr); 40 | } 41 | 42 | // MARK: helpers 43 | 44 | u64 tsnow() { 45 | struct timespec ts; 46 | // clock_gettime(CLOCK_MONOTONIC, &ts); 47 | clock_gettime(CLOCK_REALTIME, &ts); 48 | return ts.tv_sec * 1000 + ts.tv_nsec / 1e6; 49 | } 50 | 51 | bool strendswith(const char *str, const char *suffix) { 52 | size_t str_len = strlen(str); 53 | size_t suffix_len = strlen(suffix); 54 | return (str_len >= suffix_len) && (strcmp(str + str_len - suffix_len, suffix) == 0); 55 | } 56 | 57 | char *strtrim(char *str) { 58 | if (str == NULL) return NULL; 59 | 60 | char *since = str; 61 | while (isspace((unsigned char)*since)) ++since; 62 | 63 | char *until = str + strlen(str) - 1; 64 | while (until > since && isspace((unsigned char)*until)) --until; 65 | 66 | *(until + 1) = '\0'; 67 | if (since != until) memmove(str, since, until - since + 2); 68 | 69 | return str; 70 | } 71 | 72 | // MARK: random helpers 73 | 74 | static FILE *_urandom = NULL; 75 | 76 | static void _close_urandom(void) { 77 | if (_urandom != NULL) { 78 | fclose(_urandom); 79 | _urandom = NULL; 80 | } 81 | } 82 | 83 | u64 _prand64() { return (u64)rand() << 32 | (u64)rand(); } 84 | 85 | u64 _urand64() { 86 | if (_urandom == NULL) { 87 | _urandom = fopen("/dev/urandom", "rb"); 88 | if (_urandom == NULL) { 89 | fprintf(stderr, "failed to open /dev/urandom\n"); 90 | exit(1); 91 | } 92 | 93 | atexit(_close_urandom); 94 | } 95 | 96 | u64 r; 97 | if (fread(&r, sizeof(r), 1, _urandom) != 1) { 98 | fprintf(stderr, "failed to read from /dev/urandom\n"); 99 | exit(1); 100 | } 101 | 102 | return r; 103 | } 104 | 105 | INLINE u64 rand64(bool urandom) { return urandom ? _urand64() : _prand64(); } 106 | 107 | u32 encode_seed(const char *seed) { 108 | u32 hash = 0; 109 | while (*seed) { 110 | char c = *seed++; 111 | hash = (hash << 5) - hash + (unsigned char)c; 112 | hash &= 0xFFFFFFFF; 113 | } 114 | return hash; 115 | } 116 | 117 | // MARK: fe_random 118 | 119 | void fe_prand(fe r) { 120 | for (int i = 0; i < 4; ++i) r[i] = _prand64(); 121 | r[3] &= 0xfffffffefffffc2f; 122 | } 123 | 124 | void fe_urand(fe r) { 125 | for (int i = 0; i < 4; ++i) r[i] = _urand64(); 126 | r[3] &= 0xfffffffefffffc2f; 127 | } 128 | 129 | void fe_rand_range(fe r, const fe a, const fe b, bool urandom) { 130 | fe range, x; 131 | fe_modn_sub(range, b, a); // range = b - a 132 | fe_add64(range, 1); // range = range + 1 133 | 134 | size_t bits = fe_bitlen(range); 135 | assert(bits > 0 && bits <= 256); 136 | 137 | do { 138 | urandom ? fe_urand(x) : fe_prand(x); 139 | 140 | // drop unused bits 141 | int top = (bits - 1) / 64; 142 | for (int i = top + 1; i < 4; ++i) x[i] = 0; 143 | 144 | int rem = bits % 64; 145 | if (rem) x[top] &= (1ULL << rem) - 1; 146 | 147 | } while (fe_cmp(x, range) >= 0); 148 | 149 | fe_modn_add(x, x, a); 150 | assert(fe_cmp(x, a) >= 0); 151 | assert(fe_cmp(x, b) <= 0); 152 | fe_clone(r, x); 153 | } 154 | 155 | // MARK: args 156 | 157 | typedef struct args_t { 158 | int argc; 159 | const char **argv; 160 | } args_t; 161 | 162 | bool args_bool(args_t *args, const char *name) { 163 | for (int i = 1; i < args->argc; ++i) { 164 | if (strcmp(args->argv[i], name) == 0) return true; 165 | } 166 | return false; 167 | } 168 | 169 | u64 args_uint(args_t *args, const char *name, int def) { 170 | for (int i = 1; i < args->argc - 1; ++i) { 171 | if (strcmp(args->argv[i], name) == 0) { 172 | return strtoull(args->argv[i + 1], NULL, 10); 173 | } 174 | } 175 | return def; 176 | } 177 | 178 | char *arg_str(args_t *args, const char *name) { 179 | for (int i = 1; i < args->argc; ++i) { 180 | if (strcmp(args->argv[i], name) == 0) { 181 | if (i + 1 < args->argc) return (char *)args->argv[i + 1]; 182 | } 183 | } 184 | return NULL; 185 | } 186 | 187 | // MARK: queue 188 | 189 | typedef struct queue_item_t { 190 | void *data_ptr; 191 | struct queue_item_t *next; 192 | } queue_item_t; 193 | 194 | typedef struct queue_t { 195 | size_t capacity; 196 | size_t size; 197 | bool done; 198 | queue_item_t *head; 199 | queue_item_t *tail; 200 | pthread_mutex_t lock; 201 | pthread_cond_t cond_put; 202 | pthread_cond_t cond_get; 203 | } queue_t; 204 | 205 | void queue_init(queue_t *q, size_t capacity) { 206 | q->capacity = capacity; 207 | q->size = 0; 208 | q->done = false; 209 | q->head = NULL; 210 | q->tail = NULL; 211 | pthread_mutex_init(&q->lock, NULL); 212 | pthread_cond_init(&q->cond_put, NULL); 213 | pthread_cond_init(&q->cond_get, NULL); 214 | } 215 | 216 | void queue_done(queue_t *q) { 217 | pthread_mutex_lock(&q->lock); 218 | q->done = true; 219 | pthread_cond_broadcast(&q->cond_get); 220 | pthread_mutex_unlock(&q->lock); 221 | } 222 | 223 | void queue_put(queue_t *q, void *data_ptr) { 224 | pthread_mutex_lock(&q->lock); 225 | if (q->done) { 226 | pthread_mutex_unlock(&q->lock); 227 | return; 228 | } 229 | 230 | while (q->size == q->capacity) { 231 | pthread_cond_wait(&q->cond_put, &q->lock); 232 | } 233 | 234 | queue_item_t *item = malloc(sizeof(queue_item_t)); 235 | item->data_ptr = data_ptr; 236 | item->next = NULL; 237 | 238 | if (q->tail != NULL) q->tail->next = item; 239 | else q->head = item; 240 | 241 | q->tail = item; 242 | q->size += 1; 243 | 244 | pthread_cond_signal(&q->cond_get); 245 | pthread_mutex_unlock(&q->lock); 246 | } 247 | 248 | void *queue_get(queue_t *q) { 249 | pthread_mutex_lock(&q->lock); 250 | while (q->size == 0 && !q->done) { 251 | pthread_cond_wait(&q->cond_get, &q->lock); 252 | } 253 | 254 | if (q->size == 0) { 255 | pthread_mutex_unlock(&q->lock); 256 | return NULL; 257 | } 258 | 259 | queue_item_t *item = q->head; 260 | q->head = item->next; 261 | if (!q->head) q->tail = NULL; 262 | 263 | void *data_ptr = item->data_ptr; 264 | free(item); 265 | q->size -= 1; 266 | 267 | pthread_cond_signal(&q->cond_put); 268 | pthread_mutex_unlock(&q->lock); 269 | return data_ptr; 270 | } 271 | 272 | // MARK: bloom filter 273 | 274 | #define BLF_MAGIC 0x45434246 // FourCC: ECBF 275 | #define BLF_VERSION 1 276 | 277 | typedef struct blf_t { 278 | size_t size; 279 | u64 *bits; 280 | } blf_t; 281 | 282 | static inline void blf_setbit(blf_t *blf, size_t idx) { 283 | blf->bits[idx % (blf->size * 64) / 64] |= (u64)1 << (idx % 64); 284 | } 285 | 286 | static inline bool blf_getbit(blf_t *blf, u64 idx) { 287 | return (blf->bits[idx % (blf->size * 64) / 64] & ((u64)1 << (idx % 64))) != 0; 288 | } 289 | 290 | void blf_add(blf_t *blf, const h160_t hash) { 291 | u64 a1 = (u64)hash[0] << 32 | hash[1]; 292 | u64 a2 = (u64)hash[2] << 32 | hash[3]; 293 | u64 a3 = (u64)hash[4] << 32 | hash[0]; 294 | u64 a4 = (u64)hash[1] << 32 | hash[2]; 295 | u64 a5 = (u64)hash[3] << 32 | hash[4]; 296 | 297 | u8 shifts[4] = {24, 28, 36, 40}; 298 | for (size_t i = 0; i < 4; ++i) { 299 | u8 S = shifts[i]; 300 | blf_setbit(blf, a1 << S | a2 >> S); 301 | blf_setbit(blf, a2 << S | a3 >> S); 302 | blf_setbit(blf, a3 << S | a4 >> S); 303 | blf_setbit(blf, a4 << S | a5 >> S); 304 | blf_setbit(blf, a5 << S | a1 >> S); 305 | } 306 | } 307 | 308 | bool blf_has(blf_t *blf, const h160_t hash) { 309 | u64 a1 = (u64)hash[0] << 32 | hash[1]; 310 | u64 a2 = (u64)hash[2] << 32 | hash[3]; 311 | u64 a3 = (u64)hash[4] << 32 | hash[0]; 312 | u64 a4 = (u64)hash[1] << 32 | hash[2]; 313 | u64 a5 = (u64)hash[3] << 32 | hash[4]; 314 | 315 | u8 shifts[4] = {24, 28, 36, 40}; 316 | for (size_t i = 0; i < 4; ++i) { 317 | u8 S = shifts[i]; 318 | if (!blf_getbit(blf, a1 << S | a2 >> S)) return false; 319 | if (!blf_getbit(blf, a2 << S | a3 >> S)) return false; 320 | if (!blf_getbit(blf, a3 << S | a4 >> S)) return false; 321 | if (!blf_getbit(blf, a4 << S | a5 >> S)) return false; 322 | if (!blf_getbit(blf, a5 << S | a1 >> S)) return false; 323 | } 324 | 325 | return true; 326 | } 327 | 328 | bool blf_save(const char *filepath, blf_t *blf) { 329 | FILE *file = fopen(filepath, "wb"); 330 | if (file == NULL) { 331 | fprintf(stderr, "failed to open output file\n"); 332 | exit(1); 333 | } 334 | 335 | u32 blf_magic = BLF_MAGIC; 336 | u32 blg_version = BLF_VERSION; 337 | 338 | if (fwrite(&blf_magic, sizeof(blf_magic), 1, file) != 1) { 339 | fprintf(stderr, "failed to write bloom filter magic\n"); 340 | return false; 341 | }; 342 | 343 | if (fwrite(&blg_version, sizeof(blg_version), 1, file) != 1) { 344 | fprintf(stderr, "failed to write bloom filter version\n"); 345 | return false; 346 | } 347 | 348 | if (fwrite(&blf->size, sizeof(blf->size), 1, file) != 1) { 349 | fprintf(stderr, "failed to write bloom filter size\n"); 350 | return false; 351 | } 352 | 353 | if (fwrite(blf->bits, sizeof(u64), blf->size, file) != blf->size) { 354 | fprintf(stderr, "failed to write bloom filter bits\n"); 355 | return false; 356 | } 357 | 358 | fclose(file); 359 | return true; 360 | } 361 | 362 | bool blf_load(const char *filepath, blf_t *blf) { 363 | FILE *file = fopen(filepath, "rb"); 364 | if (file == NULL) { 365 | fprintf(stderr, "failed to open input file\n"); 366 | return false; 367 | } 368 | 369 | u32 blf_magic, blf_version; 370 | size_t size; 371 | 372 | bool is_ok = true; 373 | is_ok = is_ok && fread(&blf_magic, sizeof(blf_magic), 1, file) == 1; 374 | is_ok = is_ok && fread(&blf_version, sizeof(blf_version), 1, file) == 1; 375 | is_ok = is_ok && fread(&size, sizeof(size), 1, file) == 1; 376 | if (!is_ok) { 377 | fprintf(stderr, "failed to read bloom filter header\n"); 378 | return false; 379 | } 380 | 381 | if (blf_magic != BLF_MAGIC || blf_version != BLF_VERSION) { 382 | fprintf(stderr, "invalid bloom filter version; create a new filter with blf-gen command\n"); 383 | return false; 384 | } 385 | 386 | u64 *bits = calloc(size, sizeof(u64)); 387 | if (fread(bits, sizeof(u64), size, file) != size) { 388 | fprintf(stderr, "failed to read bloom filter bits\n"); 389 | return false; 390 | } 391 | 392 | fclose(file); 393 | blf->size = size; 394 | blf->bits = bits; 395 | return true; 396 | } 397 | 398 | // MARK: blf-gen command 399 | 400 | void __blf_gen_usage(args_t *args) { 401 | printf("Usage: %s blf-gen -n -o \n", args->argv[0]); 402 | printf("Generate a bloom filter from a list of hex-encoded hash160 values passed to stdin.\n"); 403 | printf("\nOptions:\n"); 404 | printf(" -n - Number of hashes to add.\n"); 405 | printf(" -o - File to write bloom filter (must have a .blf extension).\n"); 406 | exit(1); 407 | } 408 | 409 | void blf_gen(args_t *args) { 410 | u64 n = args_uint(args, "-n", 0); 411 | if (n == 0) { 412 | fprintf(stderr, "[!] missing filter size (-n )\n"); 413 | return __blf_gen_usage(args); 414 | } 415 | 416 | char *filepath = arg_str(args, "-o"); 417 | if (filepath == NULL) { 418 | fprintf(stderr, "[!] missing output file (-o )\n"); 419 | return __blf_gen_usage(args); 420 | } 421 | 422 | // https://hur.st/bloomfilter/?n=500M&p=1e9&m=&k=20 423 | u64 r = 1e9; 424 | double p = 1.0 / (double)r; 425 | u64 m = (u64)(n * log(p) / log(1.0 / pow(2.0, log(2.0)))); 426 | double mb = (double)m / 8 / 1024 / 1024; 427 | size_t size = (m + 63) / 64; 428 | 429 | blf_t blf = {.size = 0, .bits = NULL}; 430 | if (access(filepath, F_OK) == 0) { 431 | char *todo = "delete it or choose a different file"; 432 | printf("file %s already exists; loading...\n", filepath); 433 | 434 | if (!blf_load(filepath, &blf)) { 435 | fprintf(stderr, "[!] failed to load bloom filter: %s\n", todo); 436 | exit(1); 437 | } 438 | 439 | if (blf.size != size) { 440 | fprintf(stderr, "[!] bloom filter size mismatch (%'zu != %'zu): %s\n", blf.size, size, todo); 441 | exit(1); 442 | } 443 | 444 | printf("updating bloom filter...\n"); 445 | } else { 446 | printf("creating bloom filter...\n"); 447 | blf.size = size; 448 | blf.bits = calloc(blf.size, sizeof(u64)); 449 | } 450 | 451 | printf("bloom filter params: n = %'llu | p = 1:%'llu | m = %'llu (%'.1f MB)\n", n, r, m, mb); 452 | 453 | u64 count = 0; 454 | hex40 line; 455 | while (fgets(line, sizeof(line), stdin) != NULL) { 456 | if (strlen(line) != sizeof(line) - 1) continue; 457 | 458 | h160_t hash; 459 | for (size_t j = 0; j < sizeof(line) - 1; j += 8) sscanf(line + j, "%8x", &hash[j / 8]); 460 | 461 | if (blf_has(&blf, hash)) continue; 462 | 463 | blf_add(&blf, hash); 464 | count += 1; 465 | } 466 | 467 | printf("added %'llu new items; saving to %s\n", count, filepath); 468 | 469 | if (!blf_save(filepath, &blf)) { 470 | fprintf(stderr, "[!] failed to save bloom filter\n"); 471 | exit(1); 472 | } 473 | 474 | free(blf.bits); 475 | } 476 | 477 | // MARK: blf-check command 478 | 479 | void __blf_check_usage(args_t *args) { 480 | printf("Usage: %s blf-check -f [hash...]\n", args->argv[0]); 481 | printf("Check if one or more hex-encoded hash160 values are in the bloom filter.\n"); 482 | printf("\nOptions:\n"); 483 | printf(" -f Path to the bloom filter file (required).\n"); 484 | printf("\nArguments:\n"); 485 | printf(" One or more hex-encoded hash160 values to check.\n"); 486 | printf(" If no arguments are provided, stdin will be used as source.\n"); 487 | exit(1); 488 | } 489 | 490 | bool __blf_check_hex(blf_t *blf, const char *hex) { 491 | h160_t h = {0}; 492 | for (size_t i = 0; i < 40; i += 8) sscanf(hex + i, "%8x", &h[i / 8]); 493 | return blf_has(blf, h); 494 | } 495 | 496 | void blf_check(args_t *args) { 497 | char *filepath = arg_str(args, "-f"); 498 | if (filepath == NULL) { 499 | fprintf(stderr, "[!] missing input file (-f )\n"); 500 | return __blf_check_usage(args); 501 | } 502 | 503 | blf_t blf = {.size = 0, .bits = NULL}; 504 | if (!blf_load(filepath, &blf)) { 505 | fprintf(stderr, "[!] failed to load bloom filter\n"); 506 | exit(1); 507 | } 508 | 509 | bool has_opts = false; 510 | for (int i = 1; i < args->argc; ++i) { 511 | if (strlen(args->argv[i]) != 40) continue; 512 | 513 | has_opts = true; 514 | bool found = __blf_check_hex(&blf, args->argv[i]); 515 | printf("%s %s\n", args->argv[i], found ? "FOUND" : "NOT FOUND"); 516 | } 517 | 518 | if (has_opts) return; 519 | 520 | char line[128]; 521 | while (fgets(line, sizeof(line), stdin) != NULL) { 522 | strtrim(line); 523 | // printf("checking %s (%'zu)...\n", line, strlen(line)); 524 | if (strlen(line) != 40) continue; // 40 hex chars + \n 525 | 526 | bool found = __blf_check_hex(&blf, line); 527 | printf("%s %s\n", line, found ? "FOUND" : "NOT FOUND"); 528 | } 529 | } 530 | 531 | // Mark: CPU count 532 | 533 | int get_cpu_count() { 534 | #ifdef _WIN32 535 | SYSTEM_INFO sysinfo; 536 | GetSystemInfo(&sysinfo); 537 | return (int)sysinfo.dwNumberOfProcessors; 538 | #else 539 | int cpu_count = sysconf(_SC_NPROCESSORS_ONLN); 540 | return MAX(1, cpu_count); 541 | #endif 542 | } 543 | 544 | // MARK: TTY 545 | 546 | typedef void (*tty_cb_t)(void *ctx, const char ch); 547 | 548 | typedef struct { 549 | tty_cb_t cb; 550 | void *ctx; 551 | } tty_thread_args_t; 552 | 553 | #ifdef _WIN32 554 | 555 | void tty_cleanup() {} 556 | void tty_init(tty_cb_t cb, void *ctx) { atexit(tty_cleanup); } 557 | 558 | #else 559 | 560 | struct termios _orig_termios; 561 | int _tty_fd = -1; 562 | 563 | void *_tty_listener(void *arg) { 564 | tty_thread_args_t *args = (tty_thread_args_t *)arg; 565 | tty_cb_t cb = args->cb; 566 | void *ctx = args->ctx; 567 | free(args); 568 | 569 | fd_set fds; 570 | char ch; 571 | 572 | while (true) { 573 | if (_tty_fd < 0) break; 574 | 575 | // todo: race condition with tty_cleanup 576 | int tty_fd = _tty_fd; 577 | 578 | FD_ZERO(&fds); 579 | FD_SET(tty_fd, &fds); 580 | 581 | int ret = select(tty_fd + 1, &fds, NULL, NULL, NULL); 582 | if (ret < 0) break; 583 | 584 | if (FD_ISSET(tty_fd, &fds)) { 585 | if (read(tty_fd, &ch, 1) > 0) { 586 | if (cb) cb(ctx, ch); 587 | } 588 | } 589 | } 590 | 591 | return NULL; 592 | } 593 | 594 | void tty_cleanup() { 595 | if (_tty_fd < 0) return; 596 | 597 | tcsetattr(_tty_fd, TCSANOW, &_orig_termios); 598 | close(_tty_fd); 599 | _tty_fd = -1; 600 | } 601 | 602 | void tty_init(tty_cb_t cb, void *ctx) { 603 | atexit(tty_cleanup); 604 | 605 | _tty_fd = open("/dev/tty", O_RDONLY | O_NONBLOCK); 606 | if (_tty_fd < 0) return; 607 | 608 | tcgetattr(_tty_fd, &_orig_termios); 609 | 610 | struct termios raw = _orig_termios; 611 | raw.c_lflag &= ~(ICANON | ECHO); // disable canonical mode and echo 612 | tcsetattr(_tty_fd, TCSANOW, &raw); 613 | 614 | tty_thread_args_t *args = malloc(sizeof(tty_thread_args_t)); 615 | if (!args) return; 616 | args->cb = cb; 617 | args->ctx = ctx; 618 | 619 | // thread will exit when _tty_fd is closed 620 | pthread_t _tty_thread = 0; 621 | pthread_create(&_tty_thread, NULL, _tty_listener, args); 622 | } 623 | 624 | #endif 625 | -------------------------------------------------------------------------------- /lib/sha256.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) vladkens 2 | // https://github.com/vladkens/ecloop 3 | // Licensed under the MIT License. 4 | 5 | #pragma once 6 | #include "compat.c" 7 | 8 | static const u32 SHA256_K[64] = { 9 | 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, 10 | 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, 11 | 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, 12 | 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, 13 | 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, 14 | 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, 15 | 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, 16 | 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2, 17 | }; 18 | 19 | static const u32 SHA256_IV[8] = { 20 | 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, 21 | }; 22 | 23 | // https://github.com/noloader/SHA-Intrinsics/blob/master/sha256-arm.c 24 | #if defined(__ARM_NEON) && defined(__ARM_FEATURE_CRYPTO) 25 | #include 26 | 27 | void sha256_final(u32 state[8], const u8 data[], u32 length) { 28 | uint32x4_t STATE0, STATE1, ABEF_SAVE, CDGH_SAVE; 29 | uint32x4_t MSG0, MSG1, MSG2, MSG3; 30 | uint32x4_t TMP0, TMP1, TMP2; 31 | 32 | /* Load state */ 33 | // STATE0 = vld1q_u32(&state[0]); 34 | // STATE1 = vld1q_u32(&state[4]); 35 | STATE0 = vld1q_u32(&SHA256_IV[0]); 36 | STATE1 = vld1q_u32(&SHA256_IV[4]); 37 | 38 | while (length >= 64) { 39 | /* Save state */ 40 | ABEF_SAVE = STATE0; 41 | CDGH_SAVE = STATE1; 42 | 43 | /* Load message */ 44 | MSG0 = vld1q_u32((const u32 *)(data + 0)); 45 | MSG1 = vld1q_u32((const u32 *)(data + 16)); 46 | MSG2 = vld1q_u32((const u32 *)(data + 32)); 47 | MSG3 = vld1q_u32((const u32 *)(data + 48)); 48 | 49 | /* Reverse for little endian */ 50 | MSG0 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(MSG0))); 51 | MSG1 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(MSG1))); 52 | MSG2 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(MSG2))); 53 | MSG3 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(MSG3))); 54 | 55 | TMP0 = vaddq_u32(MSG0, vld1q_u32(&SHA256_K[0x00])); 56 | 57 | /* Rounds 0-3 */ 58 | MSG0 = vsha256su0q_u32(MSG0, MSG1); 59 | TMP2 = STATE0; 60 | TMP1 = vaddq_u32(MSG1, vld1q_u32(&SHA256_K[0x04])); 61 | STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0); 62 | STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0); 63 | MSG0 = vsha256su1q_u32(MSG0, MSG2, MSG3); 64 | 65 | /* Rounds 4-7 */ 66 | MSG1 = vsha256su0q_u32(MSG1, MSG2); 67 | TMP2 = STATE0; 68 | TMP0 = vaddq_u32(MSG2, vld1q_u32(&SHA256_K[0x08])); 69 | STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1); 70 | STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1); 71 | MSG1 = vsha256su1q_u32(MSG1, MSG3, MSG0); 72 | 73 | /* Rounds 8-11 */ 74 | MSG2 = vsha256su0q_u32(MSG2, MSG3); 75 | TMP2 = STATE0; 76 | TMP1 = vaddq_u32(MSG3, vld1q_u32(&SHA256_K[0x0c])); 77 | STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0); 78 | STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0); 79 | MSG2 = vsha256su1q_u32(MSG2, MSG0, MSG1); 80 | 81 | /* Rounds 12-15 */ 82 | MSG3 = vsha256su0q_u32(MSG3, MSG0); 83 | TMP2 = STATE0; 84 | TMP0 = vaddq_u32(MSG0, vld1q_u32(&SHA256_K[0x10])); 85 | STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1); 86 | STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1); 87 | MSG3 = vsha256su1q_u32(MSG3, MSG1, MSG2); 88 | 89 | /* Rounds 16-19 */ 90 | MSG0 = vsha256su0q_u32(MSG0, MSG1); 91 | TMP2 = STATE0; 92 | TMP1 = vaddq_u32(MSG1, vld1q_u32(&SHA256_K[0x14])); 93 | STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0); 94 | STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0); 95 | MSG0 = vsha256su1q_u32(MSG0, MSG2, MSG3); 96 | 97 | /* Rounds 20-23 */ 98 | MSG1 = vsha256su0q_u32(MSG1, MSG2); 99 | TMP2 = STATE0; 100 | TMP0 = vaddq_u32(MSG2, vld1q_u32(&SHA256_K[0x18])); 101 | STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1); 102 | STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1); 103 | MSG1 = vsha256su1q_u32(MSG1, MSG3, MSG0); 104 | 105 | /* Rounds 24-27 */ 106 | MSG2 = vsha256su0q_u32(MSG2, MSG3); 107 | TMP2 = STATE0; 108 | TMP1 = vaddq_u32(MSG3, vld1q_u32(&SHA256_K[0x1c])); 109 | STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0); 110 | STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0); 111 | MSG2 = vsha256su1q_u32(MSG2, MSG0, MSG1); 112 | 113 | /* Rounds 28-31 */ 114 | MSG3 = vsha256su0q_u32(MSG3, MSG0); 115 | TMP2 = STATE0; 116 | TMP0 = vaddq_u32(MSG0, vld1q_u32(&SHA256_K[0x20])); 117 | STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1); 118 | STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1); 119 | MSG3 = vsha256su1q_u32(MSG3, MSG1, MSG2); 120 | 121 | /* Rounds 32-35 */ 122 | MSG0 = vsha256su0q_u32(MSG0, MSG1); 123 | TMP2 = STATE0; 124 | TMP1 = vaddq_u32(MSG1, vld1q_u32(&SHA256_K[0x24])); 125 | STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0); 126 | STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0); 127 | MSG0 = vsha256su1q_u32(MSG0, MSG2, MSG3); 128 | 129 | /* Rounds 36-39 */ 130 | MSG1 = vsha256su0q_u32(MSG1, MSG2); 131 | TMP2 = STATE0; 132 | TMP0 = vaddq_u32(MSG2, vld1q_u32(&SHA256_K[0x28])); 133 | STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1); 134 | STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1); 135 | MSG1 = vsha256su1q_u32(MSG1, MSG3, MSG0); 136 | 137 | /* Rounds 40-43 */ 138 | MSG2 = vsha256su0q_u32(MSG2, MSG3); 139 | TMP2 = STATE0; 140 | TMP1 = vaddq_u32(MSG3, vld1q_u32(&SHA256_K[0x2c])); 141 | STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0); 142 | STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0); 143 | MSG2 = vsha256su1q_u32(MSG2, MSG0, MSG1); 144 | 145 | /* Rounds 44-47 */ 146 | MSG3 = vsha256su0q_u32(MSG3, MSG0); 147 | TMP2 = STATE0; 148 | TMP0 = vaddq_u32(MSG0, vld1q_u32(&SHA256_K[0x30])); 149 | STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1); 150 | STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1); 151 | MSG3 = vsha256su1q_u32(MSG3, MSG1, MSG2); 152 | 153 | /* Rounds 48-51 */ 154 | TMP2 = STATE0; 155 | TMP1 = vaddq_u32(MSG1, vld1q_u32(&SHA256_K[0x34])); 156 | STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0); 157 | STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0); 158 | 159 | /* Rounds 52-55 */ 160 | TMP2 = STATE0; 161 | TMP0 = vaddq_u32(MSG2, vld1q_u32(&SHA256_K[0x38])); 162 | STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1); 163 | STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1); 164 | 165 | /* Rounds 56-59 */ 166 | TMP2 = STATE0; 167 | TMP1 = vaddq_u32(MSG3, vld1q_u32(&SHA256_K[0x3c])); 168 | STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0); 169 | STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0); 170 | 171 | /* Rounds 60-63 */ 172 | TMP2 = STATE0; 173 | STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1); 174 | STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1); 175 | 176 | /* Combine state */ 177 | STATE0 = vaddq_u32(STATE0, ABEF_SAVE); 178 | STATE1 = vaddq_u32(STATE1, CDGH_SAVE); 179 | 180 | data += 64; 181 | length -= 64; 182 | } 183 | 184 | /* Save state */ 185 | vst1q_u32(&state[0], STATE0); 186 | vst1q_u32(&state[4], STATE1); 187 | } 188 | 189 | // https://github.com/noloader/SHA-Intrinsics/blob/master/sha256-x86.c 190 | #elif defined(__x86_64__) && defined(__SHA__) 191 | // #warning "SHA256: Using x86-64 with SHA intrinsics" 192 | #include 193 | 194 | void sha256_final(u32 state[8], const u8 data[], u32 length) { 195 | __m128i STATE0, STATE1; 196 | __m128i MSG, TMP; 197 | __m128i MSG0, MSG1, MSG2, MSG3; 198 | __m128i ABEF_SAVE, CDGH_SAVE; 199 | const __m128i MASK = _mm_set_epi64x(0x0c0d0e0f08090a0bULL, 0x0405060700010203ULL); 200 | 201 | /* Load initial values */ 202 | // TMP = _mm_loadu_si128((const __m128i *)&state[0]); 203 | // STATE1 = _mm_loadu_si128((const __m128i *)&state[4]); 204 | TMP = _mm_loadu_si128((const __m128i *)&SHA256_IV[0]); 205 | STATE1 = _mm_loadu_si128((const __m128i *)&SHA256_IV[4]); 206 | 207 | TMP = _mm_shuffle_epi32(TMP, 0xB1); /* CDAB */ 208 | STATE1 = _mm_shuffle_epi32(STATE1, 0x1B); /* EFGH */ 209 | STATE0 = _mm_alignr_epi8(TMP, STATE1, 8); /* ABEF */ 210 | STATE1 = _mm_blend_epi16(STATE1, TMP, 0xF0); /* CDGH */ 211 | 212 | while (length >= 64) { 213 | /* Save current state */ 214 | ABEF_SAVE = STATE0; 215 | CDGH_SAVE = STATE1; 216 | 217 | /* Rounds 0-3 */ 218 | MSG = _mm_loadu_si128((const __m128i *)(data + 0)); 219 | MSG0 = _mm_shuffle_epi8(MSG, MASK); 220 | MSG = _mm_add_epi32(MSG0, _mm_set_epi64x(0xE9B5DBA5B5C0FBCFULL, 0x71374491428A2F98ULL)); 221 | STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); 222 | MSG = _mm_shuffle_epi32(MSG, 0x0E); 223 | STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); 224 | 225 | /* Rounds 4-7 */ 226 | MSG1 = _mm_loadu_si128((const __m128i *)(data + 16)); 227 | MSG1 = _mm_shuffle_epi8(MSG1, MASK); 228 | MSG = _mm_add_epi32(MSG1, _mm_set_epi64x(0xAB1C5ED5923F82A4ULL, 0x59F111F13956C25BULL)); 229 | STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); 230 | MSG = _mm_shuffle_epi32(MSG, 0x0E); 231 | STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); 232 | MSG0 = _mm_sha256msg1_epu32(MSG0, MSG1); 233 | 234 | /* Rounds 8-11 */ 235 | MSG2 = _mm_loadu_si128((const __m128i *)(data + 32)); 236 | MSG2 = _mm_shuffle_epi8(MSG2, MASK); 237 | MSG = _mm_add_epi32(MSG2, _mm_set_epi64x(0x550C7DC3243185BEULL, 0x12835B01D807AA98ULL)); 238 | STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); 239 | MSG = _mm_shuffle_epi32(MSG, 0x0E); 240 | STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); 241 | MSG1 = _mm_sha256msg1_epu32(MSG1, MSG2); 242 | 243 | /* Rounds 12-15 */ 244 | MSG3 = _mm_loadu_si128((const __m128i *)(data + 48)); 245 | MSG3 = _mm_shuffle_epi8(MSG3, MASK); 246 | MSG = _mm_add_epi32(MSG3, _mm_set_epi64x(0xC19BF1749BDC06A7ULL, 0x80DEB1FE72BE5D74ULL)); 247 | STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); 248 | TMP = _mm_alignr_epi8(MSG3, MSG2, 4); 249 | MSG0 = _mm_add_epi32(MSG0, TMP); 250 | MSG0 = _mm_sha256msg2_epu32(MSG0, MSG3); 251 | MSG = _mm_shuffle_epi32(MSG, 0x0E); 252 | STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); 253 | MSG2 = _mm_sha256msg1_epu32(MSG2, MSG3); 254 | 255 | /* Rounds 16-19 */ 256 | MSG = _mm_add_epi32(MSG0, _mm_set_epi64x(0x240CA1CC0FC19DC6ULL, 0xEFBE4786E49B69C1ULL)); 257 | STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); 258 | TMP = _mm_alignr_epi8(MSG0, MSG3, 4); 259 | MSG1 = _mm_add_epi32(MSG1, TMP); 260 | MSG1 = _mm_sha256msg2_epu32(MSG1, MSG0); 261 | MSG = _mm_shuffle_epi32(MSG, 0x0E); 262 | STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); 263 | MSG3 = _mm_sha256msg1_epu32(MSG3, MSG0); 264 | 265 | /* Rounds 20-23 */ 266 | MSG = _mm_add_epi32(MSG1, _mm_set_epi64x(0x76F988DA5CB0A9DCULL, 0x4A7484AA2DE92C6FULL)); 267 | STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); 268 | TMP = _mm_alignr_epi8(MSG1, MSG0, 4); 269 | MSG2 = _mm_add_epi32(MSG2, TMP); 270 | MSG2 = _mm_sha256msg2_epu32(MSG2, MSG1); 271 | MSG = _mm_shuffle_epi32(MSG, 0x0E); 272 | STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); 273 | MSG0 = _mm_sha256msg1_epu32(MSG0, MSG1); 274 | 275 | /* Rounds 24-27 */ 276 | MSG = _mm_add_epi32(MSG2, _mm_set_epi64x(0xBF597FC7B00327C8ULL, 0xA831C66D983E5152ULL)); 277 | STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); 278 | TMP = _mm_alignr_epi8(MSG2, MSG1, 4); 279 | MSG3 = _mm_add_epi32(MSG3, TMP); 280 | MSG3 = _mm_sha256msg2_epu32(MSG3, MSG2); 281 | MSG = _mm_shuffle_epi32(MSG, 0x0E); 282 | STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); 283 | MSG1 = _mm_sha256msg1_epu32(MSG1, MSG2); 284 | 285 | /* Rounds 28-31 */ 286 | MSG = _mm_add_epi32(MSG3, _mm_set_epi64x(0x1429296706CA6351ULL, 0xD5A79147C6E00BF3ULL)); 287 | STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); 288 | TMP = _mm_alignr_epi8(MSG3, MSG2, 4); 289 | MSG0 = _mm_add_epi32(MSG0, TMP); 290 | MSG0 = _mm_sha256msg2_epu32(MSG0, MSG3); 291 | MSG = _mm_shuffle_epi32(MSG, 0x0E); 292 | STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); 293 | MSG2 = _mm_sha256msg1_epu32(MSG2, MSG3); 294 | 295 | /* Rounds 32-35 */ 296 | MSG = _mm_add_epi32(MSG0, _mm_set_epi64x(0x53380D134D2C6DFCULL, 0x2E1B213827B70A85ULL)); 297 | STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); 298 | TMP = _mm_alignr_epi8(MSG0, MSG3, 4); 299 | MSG1 = _mm_add_epi32(MSG1, TMP); 300 | MSG1 = _mm_sha256msg2_epu32(MSG1, MSG0); 301 | MSG = _mm_shuffle_epi32(MSG, 0x0E); 302 | STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); 303 | MSG3 = _mm_sha256msg1_epu32(MSG3, MSG0); 304 | 305 | /* Rounds 36-39 */ 306 | MSG = _mm_add_epi32(MSG1, _mm_set_epi64x(0x92722C8581C2C92EULL, 0x766A0ABB650A7354ULL)); 307 | STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); 308 | TMP = _mm_alignr_epi8(MSG1, MSG0, 4); 309 | MSG2 = _mm_add_epi32(MSG2, TMP); 310 | MSG2 = _mm_sha256msg2_epu32(MSG2, MSG1); 311 | MSG = _mm_shuffle_epi32(MSG, 0x0E); 312 | STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); 313 | MSG0 = _mm_sha256msg1_epu32(MSG0, MSG1); 314 | 315 | /* Rounds 40-43 */ 316 | MSG = _mm_add_epi32(MSG2, _mm_set_epi64x(0xC76C51A3C24B8B70ULL, 0xA81A664BA2BFE8A1ULL)); 317 | STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); 318 | TMP = _mm_alignr_epi8(MSG2, MSG1, 4); 319 | MSG3 = _mm_add_epi32(MSG3, TMP); 320 | MSG3 = _mm_sha256msg2_epu32(MSG3, MSG2); 321 | MSG = _mm_shuffle_epi32(MSG, 0x0E); 322 | STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); 323 | MSG1 = _mm_sha256msg1_epu32(MSG1, MSG2); 324 | 325 | /* Rounds 44-47 */ 326 | MSG = _mm_add_epi32(MSG3, _mm_set_epi64x(0x106AA070F40E3585ULL, 0xD6990624D192E819ULL)); 327 | STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); 328 | TMP = _mm_alignr_epi8(MSG3, MSG2, 4); 329 | MSG0 = _mm_add_epi32(MSG0, TMP); 330 | MSG0 = _mm_sha256msg2_epu32(MSG0, MSG3); 331 | MSG = _mm_shuffle_epi32(MSG, 0x0E); 332 | STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); 333 | MSG2 = _mm_sha256msg1_epu32(MSG2, MSG3); 334 | 335 | /* Rounds 48-51 */ 336 | MSG = _mm_add_epi32(MSG0, _mm_set_epi64x(0x34B0BCB52748774CULL, 0x1E376C0819A4C116ULL)); 337 | STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); 338 | TMP = _mm_alignr_epi8(MSG0, MSG3, 4); 339 | MSG1 = _mm_add_epi32(MSG1, TMP); 340 | MSG1 = _mm_sha256msg2_epu32(MSG1, MSG0); 341 | MSG = _mm_shuffle_epi32(MSG, 0x0E); 342 | STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); 343 | MSG3 = _mm_sha256msg1_epu32(MSG3, MSG0); 344 | 345 | /* Rounds 52-55 */ 346 | MSG = _mm_add_epi32(MSG1, _mm_set_epi64x(0x682E6FF35B9CCA4FULL, 0x4ED8AA4A391C0CB3ULL)); 347 | STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); 348 | TMP = _mm_alignr_epi8(MSG1, MSG0, 4); 349 | MSG2 = _mm_add_epi32(MSG2, TMP); 350 | MSG2 = _mm_sha256msg2_epu32(MSG2, MSG1); 351 | MSG = _mm_shuffle_epi32(MSG, 0x0E); 352 | STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); 353 | 354 | /* Rounds 56-59 */ 355 | MSG = _mm_add_epi32(MSG2, _mm_set_epi64x(0x8CC7020884C87814ULL, 0x78A5636F748F82EEULL)); 356 | STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); 357 | TMP = _mm_alignr_epi8(MSG2, MSG1, 4); 358 | MSG3 = _mm_add_epi32(MSG3, TMP); 359 | MSG3 = _mm_sha256msg2_epu32(MSG3, MSG2); 360 | MSG = _mm_shuffle_epi32(MSG, 0x0E); 361 | STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); 362 | 363 | /* Rounds 60-63 */ 364 | MSG = _mm_add_epi32(MSG3, _mm_set_epi64x(0xC67178F2BEF9A3F7ULL, 0xA4506CEB90BEFFFAULL)); 365 | STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); 366 | MSG = _mm_shuffle_epi32(MSG, 0x0E); 367 | STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); 368 | 369 | /* Combine state */ 370 | STATE0 = _mm_add_epi32(STATE0, ABEF_SAVE); 371 | STATE1 = _mm_add_epi32(STATE1, CDGH_SAVE); 372 | 373 | data += 64; 374 | length -= 64; 375 | } 376 | 377 | TMP = _mm_shuffle_epi32(STATE0, 0x1B); /* FEBA */ 378 | STATE1 = _mm_shuffle_epi32(STATE1, 0xB1); /* DCHG */ 379 | STATE0 = _mm_blend_epi16(TMP, STATE1, 0xF0); /* DCBA */ 380 | STATE1 = _mm_alignr_epi8(STATE1, TMP, 8); /* ABEF */ 381 | 382 | /* Save state */ 383 | _mm_storeu_si128((__m128i *)&state[0], STATE0); 384 | _mm_storeu_si128((__m128i *)&state[4], STATE1); 385 | } 386 | 387 | #else 388 | // #warning "SHA256: no intrinsics available, using fallback implementation" 389 | 390 | static inline u32 rotr(u32 x, u32 n) { return (x >> n) | (x << (32 - n)); } 391 | static inline u32 MAJ(u32 a, u32 b, u32 c) { return (a & b) ^ (a & c) ^ (b & c); } 392 | static inline u32 CH(u32 e, u32 f, u32 g) { return (e & f) ^ (~e & g); } 393 | static inline void ROUND(u32 a, u32 b, u32 c, u32 *d, u32 e, u32 f, u32 g, u32 *h, u32 m, u32 k) { 394 | u32 s = CH(e, f, g) + (rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25)) + k + m; 395 | *d += s + *h; 396 | *h += s + MAJ(a, b, c) + (rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22)); 397 | } 398 | 399 | void sha256_final(u32 state[8], const u8 data[], u32 length) { 400 | u32 s0, s1; 401 | u32 a, b, c, d, e, f, g, h; 402 | for (int i = 0; i < 8; i++) state[i] = SHA256_IV[i]; 403 | 404 | while (length >= 64) { 405 | a = state[0]; 406 | b = state[1]; 407 | c = state[2]; 408 | d = state[3]; 409 | e = state[4]; 410 | f = state[5]; 411 | g = state[6]; 412 | h = state[7]; 413 | 414 | u32 w[80] = {0}; 415 | for (int i = 0; i < 16; i++) { 416 | int k = i * 4; 417 | w[i] = (data[k] << 24) | (data[k + 1] << 16) | (data[k + 2] << 8) | data[k + 3]; 418 | } 419 | 420 | // Expand 16 words to 64 words 421 | for (int i = 16; i < 64; i++) { 422 | u32 x = w[i - 15]; 423 | u32 y = w[i - 2]; 424 | 425 | s0 = rotr(x, 7) ^ rotr(x, 18) ^ (x >> 3); 426 | s1 = rotr(y, 17) ^ rotr(y, 19) ^ (y >> 10); 427 | w[i] = w[i - 16] + s0 + w[i - 7] + s1; 428 | } 429 | 430 | for (int i = 0; i < 64; i += 8) { 431 | ROUND(a, b, c, &d, e, f, g, &h, w[i + 0], SHA256_K[i + 0]); 432 | ROUND(h, a, b, &c, d, e, f, &g, w[i + 1], SHA256_K[i + 1]); 433 | ROUND(g, h, a, &b, c, d, e, &f, w[i + 2], SHA256_K[i + 2]); 434 | ROUND(f, g, h, &a, b, c, d, &e, w[i + 3], SHA256_K[i + 3]); 435 | ROUND(e, f, g, &h, a, b, c, &d, w[i + 4], SHA256_K[i + 4]); 436 | ROUND(d, e, f, &g, h, a, b, &c, w[i + 5], SHA256_K[i + 5]); 437 | ROUND(c, d, e, &f, g, h, a, &b, w[i + 6], SHA256_K[i + 6]); 438 | ROUND(b, c, d, &e, f, g, h, &a, w[i + 7], SHA256_K[i + 7]); 439 | } 440 | 441 | state[0] += a; 442 | state[1] += b; 443 | state[2] += c; 444 | state[3] += d; 445 | state[4] += e; 446 | state[5] += f; 447 | state[6] += g; 448 | state[7] += h; 449 | 450 | data += 64; 451 | length -= 64; 452 | } 453 | } 454 | 455 | #endif 456 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) vladkens 2 | // https://github.com/vladkens/ecloop 3 | // Licensed under the MIT License. 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "lib/addr.c" 11 | #include "lib/bench.c" 12 | #include "lib/ecc.c" 13 | #include "lib/utils.c" 14 | 15 | #define VERSION "0.5.0" 16 | #define MAX_JOB_SIZE 1024 * 1024 * 2 17 | #define GROUP_INV_SIZE 2048ul 18 | #define MAX_LINE_SIZE 1025 19 | 20 | static_assert(GROUP_INV_SIZE % HASH_BATCH_SIZE == 0, 21 | "GROUP_INV_SIZE must be divisible by HASH_BATCH_SIZE"); 22 | 23 | enum Cmd { CMD_NIL, CMD_ADD, CMD_MUL, CMD_RND }; 24 | 25 | typedef struct ctx_t { 26 | enum Cmd cmd; 27 | pthread_mutex_t lock; 28 | size_t threads_count; 29 | pthread_t *threads; 30 | size_t k_checked; 31 | size_t k_found; 32 | bool check_addr33; 33 | bool check_addr65; 34 | bool use_endo; 35 | 36 | FILE *outfile; 37 | bool quiet; 38 | bool use_color; 39 | 40 | bool finished; // true if the program is exiting 41 | bool paused; // true if the program is paused 42 | size_t ts_started; // timestamp of start 43 | size_t ts_updated; // timestamp of last update 44 | size_t ts_printed; // timestamp of last print 45 | size_t ts_paused_at; // timestamp when paused 46 | size_t paused_time; // time spent in paused state 47 | 48 | // filter file (bloom filter or hashes to search) 49 | h160_t *to_find_hashes; 50 | size_t to_find_count; 51 | blf_t blf; 52 | 53 | // cmd add 54 | fe range_s; // search range start 55 | fe range_e; // search range end 56 | fe stride_k; // precomputed stride key (step for G-points, 2^offset) 57 | pe stride_p; // precomputed stride point (G * pk) 58 | pe gpoints[GROUP_INV_SIZE]; 59 | size_t job_size; 60 | 61 | // cmd mul 62 | queue_t queue; 63 | bool raw_text; 64 | 65 | // cmd rnd 66 | bool has_seed; 67 | u32 ord_offs; // offset (order) of range to search 68 | u32 ord_size; // size (span) in range to search 69 | } ctx_t; 70 | 71 | void load_filter(ctx_t *ctx, const char *filepath) { 72 | if (!filepath) { 73 | fprintf(stderr, "missing filter file\n"); 74 | exit(1); 75 | } 76 | 77 | FILE *file = fopen(filepath, "rb"); 78 | if (!file) { 79 | fprintf(stderr, "failed to open filter file: %s\n", filepath); 80 | exit(1); 81 | } 82 | 83 | char *ext = strrchr(filepath, '.'); 84 | if (ext != NULL && strcmp(ext, ".blf") == 0) { 85 | if (!blf_load(filepath, &ctx->blf)) exit(1); 86 | fclose(file); 87 | return; 88 | } 89 | 90 | size_t hlen = sizeof(u32) * 5; 91 | assert(hlen == sizeof(h160_t)); 92 | size_t capacity = 32; 93 | size_t size = 0; 94 | u32 *hashes = malloc(capacity * hlen); 95 | 96 | hex40 line; 97 | while (fgets(line, sizeof(line), file)) { 98 | if (strlen(line) != sizeof(line) - 1) continue; 99 | 100 | if (size >= capacity) { 101 | capacity *= 2; 102 | hashes = realloc(hashes, capacity * hlen); 103 | } 104 | 105 | for (size_t j = 0; j < sizeof(line) - 1; j += 8) { 106 | sscanf(line + j, "%8x", &hashes[size * 5 + j / 8]); 107 | } 108 | 109 | size += 1; 110 | } 111 | 112 | fclose(file); 113 | qsort(hashes, size, hlen, compare_160); 114 | 115 | // remove duplicates 116 | size_t unique_count = 0; 117 | for (size_t i = 1; i < size; ++i) { 118 | if (memcmp(&hashes[unique_count * 5], &hashes[i * 5], hlen) != 0) { 119 | unique_count++; 120 | memcpy(&hashes[unique_count * 5], &hashes[i * 5], hlen); 121 | } 122 | } 123 | 124 | ctx->to_find_hashes = (h160_t *)hashes; 125 | ctx->to_find_count = unique_count + 1; 126 | 127 | // generate in-memory bloom filter 128 | ctx->blf.size = ctx->to_find_count * 2; 129 | ctx->blf.bits = malloc(ctx->blf.size * sizeof(u64)); 130 | for (size_t i = 0; i < ctx->to_find_count; ++i) blf_add(&ctx->blf, hashes + i * 5); 131 | } 132 | 133 | // note: this function is not thread-safe; use mutex lock before calling 134 | void ctx_print_unlocked(ctx_t *ctx) { 135 | char *msg = ctx->finished ? "" : (ctx->paused ? " ('r' – resume)" : " ('p' – pause)"); 136 | 137 | int64_t effective_time = (int64_t)(ctx->ts_updated - ctx->ts_started) - (int64_t)ctx->paused_time; 138 | double dt = MAX(1, effective_time) / 1000.0; 139 | double it = ctx->k_checked / dt / 1000000; 140 | term_clear_line(); 141 | fprintf(stderr, "%.2fs ~ %.2f Mkeys/s ~ %'zu / %'zu%s%c", // 142 | dt, it, ctx->k_found, ctx->k_checked, msg, ctx->finished ? '\n' : '\r'); 143 | fflush(stderr); 144 | } 145 | 146 | void ctx_print_status(ctx_t *ctx) { 147 | pthread_mutex_lock(&ctx->lock); 148 | ctx_print_unlocked(ctx); 149 | pthread_mutex_unlock(&ctx->lock); 150 | } 151 | 152 | void ctx_check_paused(ctx_t *ctx) { 153 | if (ctx->paused) { 154 | while (ctx->paused) usleep(100000); 155 | } 156 | } 157 | 158 | void ctx_update(ctx_t *ctx, size_t k_checked) { 159 | size_t ts = tsnow(); 160 | 161 | pthread_mutex_lock(&ctx->lock); 162 | bool need_print = (ts - ctx->ts_printed) >= 100; 163 | ctx->k_checked += k_checked; 164 | ctx->ts_updated = ts; 165 | if (need_print) { 166 | ctx->ts_printed = ts; 167 | ctx_print_unlocked(ctx); 168 | } 169 | pthread_mutex_unlock(&ctx->lock); 170 | 171 | ctx_check_paused(ctx); 172 | } 173 | 174 | void ctx_finish(ctx_t *ctx) { 175 | pthread_mutex_lock(&ctx->lock); 176 | ctx->finished = true; 177 | ctx_print_unlocked(ctx); 178 | if (ctx->outfile != NULL) fclose(ctx->outfile); 179 | pthread_mutex_unlock(&ctx->lock); 180 | } 181 | 182 | void ctx_write_found(ctx_t *ctx, const char *label, const h160_t hash, const fe pk) { 183 | pthread_mutex_lock(&ctx->lock); 184 | 185 | if (!ctx->quiet) { 186 | term_clear_line(); 187 | printf("%s: %08x%08x%08x%08x%08x <- %016llx%016llx%016llx%016llx\n", // 188 | label, hash[0], hash[1], hash[2], hash[3], hash[4], // 189 | pk[3], pk[2], pk[1], pk[0]); 190 | } 191 | 192 | if (ctx->outfile != NULL) { 193 | fprintf(ctx->outfile, "%s\t%08x%08x%08x%08x%08x\t%016llx%016llx%016llx%016llx\n", // 194 | label, hash[0], hash[1], hash[2], hash[3], hash[4], // 195 | pk[3], pk[2], pk[1], pk[0]); 196 | fflush(ctx->outfile); 197 | } 198 | 199 | ctx->k_found += 1; 200 | ctx_print_unlocked(ctx); 201 | 202 | pthread_mutex_unlock(&ctx->lock); 203 | } 204 | 205 | bool ctx_check_hash(ctx_t *ctx, const h160_t h) { 206 | // bloom filter only mode 207 | if (ctx->to_find_hashes == NULL) { 208 | return blf_has(&ctx->blf, h); 209 | } 210 | 211 | // check by hashes list 212 | if (!blf_has(&ctx->blf, h)) return false; // fast check with bloom filter 213 | 214 | // if bloom filter check passed, do full check 215 | h160_t *rs = bsearch(h, ctx->to_find_hashes, ctx->to_find_count, sizeof(h160_t), compare_160); 216 | return rs != NULL; 217 | } 218 | 219 | void ctx_precompute_gpoints(ctx_t *ctx) { 220 | // precalc addition step with stride (2^offset) 221 | fe_set64(ctx->stride_k, 1); 222 | fe_shiftl(ctx->stride_k, ctx->ord_offs); 223 | 224 | fe t; // precalc stride point 225 | fe_modn_add_stride(t, FE_ZERO, ctx->stride_k, GROUP_INV_SIZE); 226 | ec_jacobi_mulrdc(&ctx->stride_p, &G1, t); // G * (GROUP_INV_SIZE * gs) 227 | 228 | pe g1, g2; 229 | ec_jacobi_mulrdc(&g1, &G1, ctx->stride_k); 230 | ec_jacobi_dblrdc(&g2, &g1); 231 | 232 | size_t hsize = GROUP_INV_SIZE / 2; 233 | 234 | // K+1, K+2, .., K+N/2-1 235 | pe_clone(ctx->gpoints + 0, &g1); 236 | pe_clone(ctx->gpoints + 1, &g2); 237 | for (size_t i = 2; i < hsize; ++i) { 238 | ec_jacobi_addrdc(ctx->gpoints + i, ctx->gpoints + i - 1, &g1); 239 | } 240 | 241 | // K-1, K-2, .., K-N/2 242 | for (size_t i = 0; i < hsize; ++i) { 243 | pe_clone(&ctx->gpoints[hsize + i], &ctx->gpoints[i]); 244 | fe_modp_neg(ctx->gpoints[hsize + i].y, ctx->gpoints[i].y); // y = -y 245 | } 246 | } 247 | 248 | void pk_verify_hash(const fe pk, const h160_t hash, bool c, size_t endo) { 249 | pe point; 250 | ec_jacobi_mulrdc(&point, &G1, pk); 251 | 252 | h160_t h; 253 | c ? addr33(h, &point) : addr65(h, &point); 254 | 255 | bool is_equal = memcmp(h, hash, sizeof(h160_t)) == 0; 256 | if (!is_equal) { 257 | fprintf(stderr, "[!] error: hash mismatch (compressed: %d endo: %zu)\n", c, endo); 258 | fprintf(stderr, "pk: %016llx%016llx%016llx%016llx\n", pk[3], pk[2], pk[1], pk[0]); 259 | fprintf(stderr, "lh: %08x%08x%08x%08x%08x\n", hash[0], hash[1], hash[2], hash[3], hash[4]); 260 | fprintf(stderr, "rh: %08x%08x%08x%08x%08x\n", h[0], h[1], h[2], h[3], h[4]); 261 | exit(1); 262 | } 263 | } 264 | 265 | // MARK: CMD_ADD 266 | 267 | void calc_priv(fe pk, const fe start_pk, const fe stride_k, size_t pk_off, u8 endo) { 268 | fe_modn_add_stride(pk, start_pk, stride_k, pk_off); 269 | 270 | if (endo == 0) return; 271 | if (endo == 1) fe_modn_neg(pk, pk); 272 | if (endo == 2 || endo == 3) fe_modn_mul(pk, pk, A1); 273 | if (endo == 3) fe_modn_neg(pk, pk); 274 | if (endo == 4 || endo == 5) fe_modn_mul(pk, pk, A2); 275 | if (endo == 5) fe_modn_neg(pk, pk); 276 | } 277 | 278 | void check_hash(ctx_t *ctx, bool c, const h160_t h, const fe start_pk, u64 pk_off, size_t endo) { 279 | if (!ctx_check_hash(ctx, h)) return; 280 | 281 | fe ck; 282 | calc_priv(ck, start_pk, ctx->stride_k, pk_off, endo); 283 | pk_verify_hash(ck, h, c, endo); 284 | ctx_write_found(ctx, c ? "addr33" : "addr65", h, ck); 285 | } 286 | 287 | void check_found_add(ctx_t *ctx, fe const start_pk, const pe *points) { 288 | h160_t hs33[HASH_BATCH_SIZE]; 289 | h160_t hs65[HASH_BATCH_SIZE]; 290 | 291 | for (size_t i = 0; i < GROUP_INV_SIZE; i += HASH_BATCH_SIZE) { 292 | if (ctx->check_addr33) addr33_batch(hs33, points + i, HASH_BATCH_SIZE); 293 | if (ctx->check_addr65) addr65_batch(hs65, points + i, HASH_BATCH_SIZE); 294 | for (size_t j = 0; j < HASH_BATCH_SIZE; ++j) { 295 | if (ctx->check_addr33) check_hash(ctx, true, hs33[j], start_pk, i + j, 0); 296 | if (ctx->check_addr65) check_hash(ctx, false, hs65[j], start_pk, i + j, 0); 297 | } 298 | } 299 | 300 | if (!ctx->use_endo) return; 301 | 302 | // https://bitcointalk.org/index.php?topic=5527935.msg65000919#msg65000919 303 | // PubKeys = (x,y) (x,-y) (x*beta,y) (x*beta,-y) (x*beta^2,y) (x*beta^2,-y) 304 | // PrivKeys = (pk) (!pk) (pk*alpha) !(pk*alpha) (pk*alpha^2) !(pk*alpha^2) 305 | 306 | size_t esize = HASH_BATCH_SIZE * 5; 307 | pe endos[esize]; 308 | for (size_t i = 0; i < esize; ++i) fe_set64(endos[i].z, 1); 309 | 310 | size_t ci = 0; 311 | for (size_t k = 0; k < GROUP_INV_SIZE; ++k) { 312 | size_t idx = (k * 5) % esize; 313 | 314 | fe_clone(endos[idx + 0].x, points[k].x); // (x, -y) 315 | fe_modp_neg(endos[idx + 0].y, points[k].y); 316 | 317 | fe_modp_mul(endos[idx + 1].x, points[k].x, B1); // (x * beta, y) 318 | fe_clone(endos[idx + 1].y, points[k].y); 319 | 320 | fe_clone(endos[idx + 2].x, endos[idx + 1].x); // (x * beta, -y) 321 | fe_clone(endos[idx + 2].y, endos[idx + 0].y); 322 | 323 | fe_modp_mul(endos[idx + 3].x, points[k].x, B2); // (x * beta^2, y) 324 | fe_clone(endos[idx + 3].y, points[k].y); 325 | 326 | fe_clone(endos[idx + 4].x, endos[idx + 3].x); // (x * beta^2, -y) 327 | fe_clone(endos[idx + 4].y, endos[idx + 0].y); 328 | 329 | bool is_full = (idx + 5) % esize == 0 || k == GROUP_INV_SIZE - 1; 330 | if (!is_full) continue; 331 | 332 | for (size_t i = 0; i < esize; i += HASH_BATCH_SIZE) { 333 | if (ctx->check_addr33) addr33_batch(hs33, endos + i, HASH_BATCH_SIZE); 334 | if (ctx->check_addr65) addr65_batch(hs65, endos + i, HASH_BATCH_SIZE); 335 | 336 | for (size_t j = 0; j < HASH_BATCH_SIZE; ++j) { 337 | // if (ci >= (GROUP_INV_SIZE * 5)) break; 338 | // printf(">> %6zu | %6zu ~ %zu\n", ci, ci / 5, (ci % 5) + 1); 339 | if (ctx->check_addr33) check_hash(ctx, true, hs33[j], start_pk, ci / 5, (ci % 5) + 1); 340 | if (ctx->check_addr65) check_hash(ctx, false, hs65[j], start_pk, ci / 5, (ci % 5) + 1); 341 | ci += 1; 342 | } 343 | } 344 | } 345 | 346 | assert(ci == GROUP_INV_SIZE * 5); 347 | } 348 | 349 | void batch_add(ctx_t *ctx, const fe pk, const size_t iterations) { 350 | size_t hsize = GROUP_INV_SIZE / 2; 351 | 352 | pe bp[GROUP_INV_SIZE]; // calculated ec points 353 | fe dx[hsize]; // delta x for group inversion 354 | pe GStart; // iteration points 355 | fe ck, rx, ry; // current start point; tmp for x3, y3 356 | fe ss, dd; // temp variables 357 | 358 | // set start point to center of the group 359 | fe_modn_add_stride(ss, pk, ctx->stride_k, hsize); 360 | ec_jacobi_mulrdc(&GStart, &G1, ss); // G * (pk + hsize * gs) 361 | 362 | // group addition with single inversion (with stride support) 363 | // structure: K-N/2 .. K-2 K-1 [K] K+1 K+2 .. K+N/2-1 (last K dropped to have odd size) 364 | // points in `bp` already order by `pk` increment 365 | fe_clone(ck, pk); // start pk for current iteration 366 | 367 | size_t counter = 0; 368 | while (counter < iterations) { 369 | for (size_t i = 0; i < hsize; ++i) fe_modp_sub(dx[i], ctx->gpoints[i].x, GStart.x); 370 | fe_modp_grpinv(dx, hsize); 371 | 372 | pe_clone(&bp[hsize + 0], &GStart); // set K value 373 | 374 | for (size_t D = 0; D < 2; ++D) { 375 | bool positive = D == 0; 376 | size_t g_idx = positive ? 0 : hsize; // plus points in first half, minus in second half 377 | size_t g_max = positive ? hsize - 1 : hsize; // skip K+N/2, since we don't need it 378 | for (size_t i = 0; i < g_max; ++i) { 379 | fe_modp_sub(ss, ctx->gpoints[g_idx + i].y, GStart.y); // y2 - y1 380 | fe_modp_mul(ss, ss, dx[i]); // λ = (y2 - y1) / (x2 - x1) 381 | fe_modp_sqr(rx, ss); // λ² 382 | fe_modp_sub(rx, rx, GStart.x); // λ² - x1 383 | fe_modp_sub(rx, rx, ctx->gpoints[g_idx + i].x); // rx = λ² - x1 - x2 384 | fe_modp_sub(dd, GStart.x, rx); // x1 - rx 385 | fe_modp_mul(dd, ss, dd); // λ * (x1 - rx) 386 | fe_modp_sub(ry, dd, GStart.y); // ry = λ * (x1 - rx) - y1 387 | 388 | // ordered by pk: 389 | // [0]: K-N/2, [1]: K-N/2+1, .., [N/2-1]: K-1 // all minus points 390 | // [N/2]: K, [N/2+1]: K+1, .., [N-1]: K+N/2-1 // K, plus points without last element 391 | size_t idx = positive ? hsize + i + 1 : hsize - 1 - i; 392 | fe_clone(bp[idx].x, rx); 393 | fe_clone(bp[idx].y, ry); 394 | fe_set64(bp[idx].z, 0x1); 395 | } 396 | } 397 | 398 | check_found_add(ctx, ck, bp); 399 | fe_modn_add_stride(ck, ck, ctx->stride_k, GROUP_INV_SIZE); // move pk to next group START 400 | ec_jacobi_addrdc(&GStart, &GStart, &ctx->stride_p); // move GStart to next group CENTER 401 | counter += GROUP_INV_SIZE; 402 | } 403 | } 404 | 405 | void *cmd_add_worker(void *arg) { 406 | ctx_t *ctx = (ctx_t *)arg; 407 | 408 | fe initial_r; // keep initial range start to check overflow 409 | fe_clone(initial_r, ctx->range_s); 410 | 411 | // job_size multiply by 2^offset (iterate over desired digit order) 412 | // for example: 3013 3023 .. 30X3 .. 3093 3103 3113 413 | fe inc = {0}; 414 | fe_set64(inc, ctx->job_size); 415 | fe_modn_mul(inc, inc, ctx->stride_k); 416 | 417 | fe pk; 418 | while (true) { 419 | pthread_mutex_lock(&ctx->lock); 420 | bool is_overflow = fe_cmp(ctx->range_s, initial_r) < 0; 421 | if (fe_cmp(ctx->range_s, ctx->range_e) >= 0 || is_overflow) { 422 | pthread_mutex_unlock(&ctx->lock); 423 | break; 424 | } 425 | 426 | fe_clone(pk, ctx->range_s); 427 | fe_modn_add(ctx->range_s, ctx->range_s, inc); 428 | pthread_mutex_unlock(&ctx->lock); 429 | 430 | batch_add(ctx, pk, ctx->job_size); 431 | ctx_update(ctx, ctx->use_endo ? ctx->job_size * 6 : ctx->job_size); 432 | } 433 | 434 | return NULL; 435 | } 436 | 437 | void cmd_add(ctx_t *ctx) { 438 | ctx_precompute_gpoints(ctx); 439 | 440 | fe range_size; 441 | fe_modn_sub(range_size, ctx->range_e, ctx->range_s); 442 | ctx->job_size = fe_cmp64(range_size, MAX_JOB_SIZE) < 0 ? range_size[0] : MAX_JOB_SIZE; 443 | ctx->ts_started = tsnow(); // actual start time 444 | 445 | for (size_t i = 0; i < ctx->threads_count; ++i) { 446 | pthread_create(&ctx->threads[i], NULL, cmd_add_worker, ctx); 447 | } 448 | 449 | for (size_t i = 0; i < ctx->threads_count; ++i) { 450 | pthread_join(ctx->threads[i], NULL); 451 | } 452 | 453 | ctx_finish(ctx); 454 | } 455 | 456 | // MARK: CMD_MUL 457 | 458 | void check_found_mul(ctx_t *ctx, const fe *pk, const pe *cp, size_t cnt) { 459 | h160_t hs33[HASH_BATCH_SIZE]; 460 | h160_t hs65[HASH_BATCH_SIZE]; 461 | 462 | for (size_t i = 0; i < cnt; i += HASH_BATCH_SIZE) { 463 | size_t batch_size = MIN(HASH_BATCH_SIZE, cnt - i); 464 | if (ctx->check_addr33) addr33_batch(hs33, cp + i, batch_size); 465 | if (ctx->check_addr65) addr65_batch(hs65, cp + i, batch_size); 466 | 467 | for (size_t j = 0; j < HASH_BATCH_SIZE; ++j) { 468 | if (ctx->check_addr33 && ctx_check_hash(ctx, hs33[j])) { 469 | // pk_verify_hash(pk[i + j], hs33[j], true, 0); 470 | ctx_write_found(ctx, "addr33", hs33[j], pk[i + j]); 471 | } 472 | 473 | if (ctx->check_addr65 && ctx_check_hash(ctx, hs65[j])) { 474 | // pk_verify_hash(pk[i + j], hs65[j], false, 0); 475 | ctx_write_found(ctx, "addr65", hs65[j], pk[i + j]); 476 | } 477 | } 478 | } 479 | } 480 | 481 | typedef struct cmd_mul_job_t { 482 | size_t count; 483 | char lines[GROUP_INV_SIZE][MAX_LINE_SIZE]; 484 | } cmd_mul_job_t; 485 | 486 | void *cmd_mul_worker(void *arg) { 487 | ctx_t *ctx = (ctx_t *)arg; 488 | 489 | // sha256 routine 490 | u8 msg[(MAX_LINE_SIZE + 63 + 9) / 64 * 64] = {0}; // 9 = 1 byte 0x80 + 8 byte bitlen 491 | u32 res[8] = {0}; 492 | 493 | fe pk[GROUP_INV_SIZE]; 494 | pe cp[GROUP_INV_SIZE]; 495 | cmd_mul_job_t *job = NULL; 496 | 497 | while (true) { 498 | if (job != NULL) free(job); 499 | job = queue_get(&ctx->queue); 500 | if (job == NULL) break; 501 | 502 | // parse private keys from hex string 503 | if (!ctx->raw_text) { 504 | for (size_t i = 0; i < job->count; ++i) fe_modn_from_hex(pk[i], job->lines[i]); 505 | } else { 506 | for (size_t i = 0; i < job->count; ++i) { 507 | size_t len = strlen(job->lines[i]); 508 | size_t msg_size = (len + 63 + 9) / 64 * 64; 509 | 510 | // calculate sha256 hash 511 | size_t bitlen = len * 8; 512 | memcpy(msg, job->lines[i], len); 513 | memset(msg + len, 0, msg_size - len); 514 | msg[len] = 0x80; 515 | for (int j = 0; j < 8; j++) msg[msg_size - 1 - j] = bitlen >> (j * 8); 516 | sha256_final(res, (u8 *)msg, msg_size); 517 | 518 | // debug log (do with `-t 1`) 519 | // printf("\n%zu %s\n", len, job->lines[i]); 520 | // for (int i = 0; i < msg_size; i++) printf("%02x%s", msg[i], i % 16 == 15 ? "\n" : " "); 521 | // for (int i = 0; i < 8; i++) printf("%08x%s", res[i], i % 8 == 7 ? "\n" : ""); 522 | 523 | pk[i][0] = (u64)res[6] << 32 | res[7]; 524 | pk[i][1] = (u64)res[4] << 32 | res[5]; 525 | pk[i][2] = (u64)res[2] << 32 | res[3]; 526 | pk[i][3] = (u64)res[0] << 32 | res[1]; 527 | } 528 | } 529 | 530 | // compute public keys in batch 531 | for (size_t i = 0; i < job->count; ++i) ec_gtable_mul(&cp[i], pk[i]); 532 | ec_jacobi_grprdc(cp, job->count); 533 | 534 | check_found_mul(ctx, pk, cp, job->count); 535 | ctx_update(ctx, job->count); 536 | } 537 | 538 | if (job != NULL) free(job); 539 | return NULL; 540 | } 541 | 542 | void cmd_mul(ctx_t *ctx) { 543 | ec_gtable_init(); 544 | 545 | for (size_t i = 0; i < ctx->threads_count; ++i) { 546 | pthread_create(&ctx->threads[i], NULL, cmd_mul_worker, ctx); 547 | } 548 | 549 | cmd_mul_job_t *job = calloc(1, sizeof(cmd_mul_job_t)); 550 | char line[MAX_LINE_SIZE]; 551 | 552 | while (fgets(line, sizeof(line), stdin) != NULL) { 553 | size_t len = strlen(line); 554 | if (len && line[len - 1] == '\n') line[--len] = '\0'; 555 | if (len && line[len - 1] == '\r') line[--len] = '\0'; 556 | if (len == 0) continue; 557 | 558 | strcpy(job->lines[job->count++], line); 559 | if (job->count == GROUP_INV_SIZE) { 560 | queue_put(&ctx->queue, job); 561 | job = calloc(1, sizeof(cmd_mul_job_t)); 562 | } 563 | } 564 | 565 | if (job->count > 0 && job->count != GROUP_INV_SIZE) { 566 | queue_put(&ctx->queue, job); 567 | } 568 | 569 | queue_done(&ctx->queue); 570 | 571 | for (size_t i = 0; i < ctx->threads_count; ++i) { 572 | pthread_join(ctx->threads[i], NULL); 573 | } 574 | 575 | ctx_finish(ctx); 576 | } 577 | 578 | // MARK: CMD_RND 579 | 580 | void gen_random_range(ctx_t *ctx, const fe a, const fe b) { 581 | fe_rand_range(ctx->range_s, a, b, !ctx->has_seed); 582 | fe_clone(ctx->range_e, ctx->range_s); 583 | for (u32 i = ctx->ord_offs; i < (ctx->ord_offs + ctx->ord_size); ++i) { 584 | ctx->range_s[i / 64] &= ~(1ULL << (i % 64)); 585 | ctx->range_e[i / 64] |= 1ULL << (i % 64); 586 | } 587 | 588 | // put in bounds 589 | if (fe_cmp(ctx->range_s, a) <= 0) fe_clone(ctx->range_s, a); 590 | if (fe_cmp(ctx->range_e, b) >= 0) fe_clone(ctx->range_e, b); 591 | } 592 | 593 | void print_range_mask(fe range_s, u32 bits_size, u32 offset, bool use_color) { 594 | int mask_e = 255 - offset; 595 | int mask_s = mask_e - bits_size + 1; 596 | 597 | for (int i = 0; i < 64; i++) { 598 | if (i % 16 == 0 && i != 0) putchar(' '); 599 | 600 | int bits_s = i * 4; 601 | int bits_e = bits_s + 3; 602 | 603 | u32 fcc = (range_s[(255 - bits_e) / 64] >> ((255 - bits_e) % 64)) & 0xF; 604 | char cc = "0123456789abcdef"[fcc]; 605 | 606 | bool flag = (bits_s >= mask_s && bits_s <= mask_e) || (bits_e >= mask_s && bits_e <= mask_e); 607 | if (flag) { 608 | if (use_color) fputs(COLOR_YELLOW, stdout); 609 | putchar(cc); 610 | if (use_color) fputs(COLOR_RESET, stdout); 611 | } else { 612 | putchar(cc); 613 | } 614 | } 615 | 616 | putchar('\n'); 617 | } 618 | 619 | void cmd_rnd(ctx_t *ctx) { 620 | ctx->ord_offs = MIN(ctx->ord_offs, 255 - ctx->ord_size); 621 | printf("[RANDOM MODE] offs: %d ~ bits: %d\n\n", ctx->ord_offs, ctx->ord_size); 622 | 623 | ctx_precompute_gpoints(ctx); 624 | ctx->job_size = MAX_JOB_SIZE; 625 | ctx->ts_started = tsnow(); // actual start time 626 | 627 | fe range_s, range_e; 628 | fe_clone(range_s, ctx->range_s); 629 | fe_clone(range_e, ctx->range_e); 630 | 631 | size_t last_c = 0, last_f = 0, s_time = 0; 632 | while (true) { 633 | last_c = ctx->k_checked; 634 | last_f = ctx->k_found; 635 | s_time = tsnow(); 636 | 637 | gen_random_range(ctx, range_s, range_e); 638 | print_range_mask(ctx->range_s, ctx->ord_size, ctx->ord_offs, ctx->use_color); 639 | print_range_mask(ctx->range_e, ctx->ord_size, ctx->ord_offs, ctx->use_color); 640 | ctx_print_status(ctx); 641 | 642 | // if full range is used, skip break after first iteration 643 | bool is_full = fe_cmp(ctx->range_s, range_s) == 0 && fe_cmp(ctx->range_e, range_e) == 0; 644 | 645 | for (size_t i = 0; i < ctx->threads_count; ++i) { 646 | pthread_create(&ctx->threads[i], NULL, cmd_add_worker, ctx); 647 | } 648 | 649 | for (size_t i = 0; i < ctx->threads_count; ++i) { 650 | pthread_join(ctx->threads[i], NULL); 651 | } 652 | 653 | size_t dc = ctx->k_checked - last_c, df = ctx->k_found - last_f; 654 | double dt = MAX((tsnow() - s_time), 1ul) / 1000.0; 655 | term_clear_line(); 656 | printf("%'zu / %'zu ~ %.1fs\n\n", df, dc, dt); 657 | 658 | if (is_full) break; 659 | } 660 | 661 | ctx_finish(ctx); 662 | } 663 | 664 | // MARK: args helpers 665 | 666 | void arg_search_range(args_t *args, fe range_s, fe range_e) { 667 | char *raw = arg_str(args, "-r"); 668 | if (!raw) { 669 | fe_set64(range_s, GROUP_INV_SIZE); 670 | fe_clone(range_e, FE_P); 671 | return; 672 | } 673 | 674 | char *sep = strchr(raw, ':'); 675 | if (!sep) { 676 | fprintf(stderr, "invalid search range, use format: -r 8000:ffff\n"); 677 | exit(1); 678 | } 679 | 680 | *sep = 0; 681 | fe_modn_from_hex(range_s, raw); 682 | fe_modn_from_hex(range_e, sep + 1); 683 | 684 | // if (fe_cmp64(range_s, GROUP_INV_SIZE) <= 0) fe_set64(range_s, GROUP_INV_SIZE + 1); 685 | // if (fe_cmp(range_e, FE_P) > 0) fe_clone(range_e, FE_P); 686 | 687 | if (fe_cmp64(range_s, GROUP_INV_SIZE) <= 0) { 688 | fprintf(stderr, "invalid search range, start <= %#lx\n", GROUP_INV_SIZE); 689 | exit(1); 690 | } 691 | 692 | if (fe_cmp(range_e, FE_P) > 0) { 693 | fprintf(stderr, "invalid search range, end > FE_P\n"); 694 | exit(1); 695 | } 696 | 697 | if (fe_cmp(range_s, range_e) >= 0) { 698 | fprintf(stderr, "invalid search range, start >= end\n"); 699 | exit(1); 700 | } 701 | } 702 | 703 | void load_offs_size(ctx_t *ctx, args_t *args) { 704 | const u32 MIN_SIZE = 20; 705 | const u32 MAX_SIZE = 64; 706 | 707 | u32 range_bits = fe_bitlen(ctx->range_e); 708 | u32 default_bits = range_bits < 32 ? MAX(MIN_SIZE, range_bits) : 32; 709 | u32 max_offs = MAX(1ul, MAX(MIN_SIZE, range_bits) - default_bits); 710 | 711 | char *raw = arg_str(args, "-d"); 712 | if (!raw && ctx->cmd == CMD_RND) { 713 | ctx->ord_offs = rand64(!ctx->has_seed) % max_offs; 714 | ctx->ord_size = default_bits; 715 | return; 716 | } 717 | 718 | if (!raw) { 719 | ctx->ord_offs = 0; 720 | ctx->ord_size = default_bits; 721 | return; 722 | } 723 | 724 | char *sep = strchr(raw, ':'); 725 | if (!sep) { 726 | fprintf(stderr, "invalid offset:size format, use format: -d 128:32\n"); 727 | exit(1); 728 | } 729 | 730 | *sep = 0; 731 | u32 tmp_offs = atoi(raw); 732 | u32 tmp_size = atoi(sep + 1); 733 | 734 | if (tmp_offs > 255) { 735 | fprintf(stderr, "invalid offset, max is 255\n"); 736 | exit(1); 737 | } 738 | 739 | if (tmp_size < MIN_SIZE || tmp_size > MAX_SIZE) { 740 | fprintf(stderr, "invalid size, min is %d and max is %d\n", MIN_SIZE, MAX_SIZE); 741 | exit(1); 742 | } 743 | 744 | ctx->ord_offs = MIN(max_offs, tmp_offs); 745 | ctx->ord_size = tmp_size; 746 | } 747 | 748 | // MARK: main 749 | 750 | void usage(const char *name) { 751 | printf("Usage: %s [-t ] [-f ] [-a ] [-r ]\n", name); 752 | printf("v%s ~ https://github.com/vladkens/ecloop\n", VERSION); 753 | printf("\nCompute commands:\n"); 754 | printf(" add - search in given range with batch addition\n"); 755 | printf(" mul - search hex encoded private keys (from stdin)\n"); 756 | printf(" rnd - search random range of bits in given range\n"); 757 | printf("\nCompute options:\n"); 758 | printf(" -f - filter file to search (list of hashes or bloom fitler)\n"); 759 | printf(" -o - output file to write found keys (default: stdout)\n"); 760 | printf(" -t - number of threads to run (default: 1)\n"); 761 | printf(" -a - address type to search: c - addr33, u - addr65 (default: c)\n"); 762 | printf(" -r - search range in hex format (example: 8000:ffff, default all)\n"); 763 | printf(" -d - bit offset and size for search (example: 128:32, default: 0:32)\n"); 764 | printf(" -q - quiet mode (no output to stdout; -o required)\n"); 765 | printf(" -endo - use endomorphism (default: false)\n"); 766 | printf("\nOther commands:\n"); 767 | printf(" blf-gen - create bloom filter from list of hex-encoded hash160\n"); 768 | printf(" blf-check - check bloom filter for given hex-encoded hash160\n"); 769 | printf(" bench - run benchmark of internal functions\n"); 770 | printf(" bench-gtable - run benchmark of ecc multiplication (with different table size)\n"); 771 | printf("\n"); 772 | } 773 | 774 | void init(ctx_t *ctx, args_t *args) { 775 | // check other commands first 776 | if (args->argc > 1) { 777 | if (strcmp(args->argv[1], "blf-gen") == 0) return blf_gen(args); 778 | if (strcmp(args->argv[1], "blf-check") == 0) return blf_check(args); 779 | if (strcmp(args->argv[1], "bench") == 0) return run_bench(); 780 | if (strcmp(args->argv[1], "bench-gtable") == 0) return run_bench_gtable(); 781 | if (strcmp(args->argv[1], "mult-verify") == 0) return mult_verify(); 782 | } 783 | 784 | ctx->use_color = isatty(fileno(stdout)); 785 | 786 | ctx->cmd = CMD_NIL; // default show help 787 | if (args->argc > 1) { 788 | if (strcmp(args->argv[1], "add") == 0) ctx->cmd = CMD_ADD; 789 | if (strcmp(args->argv[1], "mul") == 0) ctx->cmd = CMD_MUL; 790 | if (strcmp(args->argv[1], "rnd") == 0) ctx->cmd = CMD_RND; 791 | } 792 | 793 | if (ctx->cmd == CMD_NIL) { 794 | if (args_bool(args, "-v")) printf("ecloop v%s\n", VERSION); 795 | else usage(args->argv[0]); 796 | exit(0); 797 | } 798 | 799 | ctx->has_seed = false; 800 | char *seed = arg_str(args, "-seed"); 801 | if (seed != NULL) { 802 | ctx->has_seed = true; 803 | srand(encode_seed(seed)); 804 | free(seed); 805 | } 806 | 807 | char *path = arg_str(args, "-f"); 808 | load_filter(ctx, path); 809 | 810 | ctx->quiet = args_bool(args, "-q"); 811 | char *outfile = arg_str(args, "-o"); 812 | if (outfile) ctx->outfile = fopen(outfile, "a"); 813 | 814 | if (outfile == NULL && ctx->quiet) { 815 | fprintf(stderr, "quiet mode chosen without output file\n"); 816 | exit(1); 817 | } 818 | 819 | char *addr = arg_str(args, "-a"); 820 | if (addr) { 821 | ctx->check_addr33 = strstr(addr, "c") != NULL; 822 | ctx->check_addr65 = strstr(addr, "u") != NULL; 823 | } 824 | 825 | if (!ctx->check_addr33 && !ctx->check_addr65) { 826 | ctx->check_addr33 = true; // default to addr33 827 | } 828 | 829 | ctx->use_endo = args_bool(args, "-endo"); 830 | if (ctx->cmd == CMD_MUL) ctx->use_endo = false; // no endo for mul command 831 | 832 | pthread_mutex_init(&ctx->lock, NULL); 833 | int cpus = get_cpu_count(); 834 | ctx->threads_count = MIN(MAX(args_uint(args, "-t", cpus), 1ul), 320ul); 835 | ctx->threads = malloc(ctx->threads_count * sizeof(pthread_t)); 836 | ctx->finished = false; 837 | ctx->k_checked = 0; 838 | ctx->k_found = 0; 839 | ctx->ts_started = tsnow(); 840 | ctx->ts_updated = ctx->ts_started; 841 | ctx->ts_printed = ctx->ts_started - 5e3; 842 | ctx->paused_time = 0; 843 | ctx->paused = false; 844 | 845 | arg_search_range(args, ctx->range_s, ctx->range_e); 846 | load_offs_size(ctx, args); 847 | queue_init(&ctx->queue, ctx->threads_count * 3); 848 | 849 | printf("threads: %zu ~ addr33: %d ~ addr65: %d ~ endo: %d | filter: ", // 850 | ctx->threads_count, ctx->check_addr33, ctx->check_addr65, ctx->use_endo); 851 | 852 | if (ctx->to_find_hashes != NULL) printf("list (%'zu)\n", ctx->to_find_count); 853 | else printf("bloom\n"); 854 | 855 | if (ctx->cmd == CMD_ADD) { 856 | fe_print("range_s", ctx->range_s); 857 | fe_print("range_e", ctx->range_e); 858 | } 859 | 860 | if (ctx->cmd == CMD_MUL) { 861 | ctx->raw_text = args_bool(args, "-raw"); 862 | } 863 | 864 | printf("----------------------------------------\n"); 865 | } 866 | 867 | void handle_sigint(int sig) { 868 | fflush(stderr); 869 | fflush(stdout); 870 | printf("\n"); 871 | exit(sig); 872 | } 873 | 874 | void tty_cb(void *ctx_raw, const char ch) { 875 | ctx_t *ctx = (ctx_t *)ctx_raw; 876 | 877 | if (ch == 'p' && !ctx->paused) { 878 | ctx->ts_paused_at = tsnow(); 879 | ctx->paused = true; 880 | ctx_print_status(ctx); 881 | } 882 | 883 | if (ch == 'r' && ctx->paused) { 884 | ctx->paused_time += tsnow() - ctx->ts_paused_at; 885 | ctx->paused = false; 886 | ctx_print_status(ctx); 887 | } 888 | } 889 | 890 | int main(int argc, const char **argv) { 891 | // https://stackoverflow.com/a/11695246 892 | setlocale(LC_NUMERIC, ""); // for comma separated numbers 893 | args_t args = {argc, argv}; 894 | 895 | ctx_t ctx = {0}; 896 | init(&ctx, &args); 897 | 898 | signal(SIGINT, handle_sigint); // Keep last progress line on Ctrl-C 899 | tty_init(tty_cb, &ctx); // override tty to handle pause/resume 900 | 901 | if (ctx.cmd == CMD_ADD) cmd_add(&ctx); 902 | if (ctx.cmd == CMD_MUL) cmd_mul(&ctx); 903 | if (ctx.cmd == CMD_RND) cmd_rnd(&ctx); 904 | 905 | return 0; 906 | } 907 | -------------------------------------------------------------------------------- /lib/ecc.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) vladkens 2 | // https://github.com/vladkens/ecloop 3 | // Licensed under the MIT License. 4 | 5 | #pragma once 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "compat.c" 15 | #define GLOBAL static const 16 | 17 | INLINE u64 umul128(const u64 a, const u64 b, u64 *hi) { 18 | // https://stackoverflow.com/a/50958815 19 | // https://botan.randombit.net/doxygen/mul128_8h_source.html 20 | u128 t = (u128)a * b; 21 | *hi = t >> 64; 22 | return t; 23 | } 24 | 25 | // MARK: Field Element 26 | typedef u64 fe[4]; // 256bit as 4x64bit (a0 + a1*2^64 + a2*2^128 + a3*2^192) 27 | typedef u64 fe320[5]; // 320bit as 5x64bit (a0 + a1*2^64 + a2*2^128 + a3*2^192 + a4*2^256) 28 | 29 | GLOBAL fe FE_ZERO = {0, 0, 0, 0}; 30 | 31 | // Secp256k1 prime field (2^256 - 2^32 - 977) and order 32 | GLOBAL fe FE_P = {0xfffffffefffffc2f, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff}; 33 | GLOBAL fe FE_N = {0xbfd25e8cd0364141, 0xbaaedce6af48a03b, 0xfffffffffffffffe, 0xffffffffffffffff}; 34 | 35 | // endomorphism constants (alpha, alpha^2, beta, beta^2) 36 | GLOBAL fe A1 = {0xdf02967c1b23bd72, 0x122e22ea20816678, 0xa5261c028812645a, 0x5363ad4cc05c30e0}; 37 | GLOBAL fe A2 = {0xe0cfc810b51283ce, 0xa880b9fc8ec739c2, 0x5ad9e3fd77ed9ba4, 0xac9c52b33fa3cf1f}; 38 | GLOBAL fe B1 = {0xc1396c28719501ee, 0x9cf0497512f58995, 0x6e64479eac3434e9, 0x7ae96a2b657c0710}; 39 | GLOBAL fe B2 = {0x3ec693d68e6afa40, 0x630fb68aed0a766a, 0x919bb86153cbcb16, 0x851695d49a83f8ef}; 40 | 41 | INLINE void fe_print(const char *label, const fe a) { 42 | printf("%s: %016llx %016llx %016llx %016llx\n", label, a[3], a[2], a[1], a[0]); 43 | } 44 | 45 | INLINE bool fe_iszero(const fe r) { return r[0] == 0 && r[1] == 0 && r[2] == 0 && r[3] == 0; } 46 | INLINE void fe_clone(fe r, const fe a) { memcpy(r, a, sizeof(fe)); } 47 | INLINE void fe_set64(fe r, const u64 a) { 48 | memset(r, 0, sizeof(fe)); 49 | r[0] = a; 50 | } 51 | 52 | size_t fe_bitlen(const fe a) { 53 | for (int i = 3; i >= 0; --i) { 54 | if (a[i]) return 64 * i + (64 - __builtin_clzll(a[i])); 55 | } 56 | return 0; 57 | } 58 | 59 | void fe_add64(fe r, const u64 a) { 60 | u64 c = 0; 61 | r[0] = addc64(r[0], a, 0, &c); 62 | r[1] = addc64(r[1], 0, c, &c); 63 | r[2] = addc64(r[2], 0, c, &c); 64 | r[3] = addc64(r[3], 0, c, &c); 65 | } 66 | 67 | int fe_cmp64(const fe a, const u64 b) { 68 | if (a[3] != 0 || a[2] != 0 || a[1] != 0) return 1; 69 | if (a[0] != b) return a[0] > b ? 1 : -1; 70 | return 0; 71 | } 72 | 73 | int fe_cmp(const fe a, const fe b) { 74 | if (a[3] != b[3]) return a[3] > b[3] ? 1 : -1; 75 | if (a[2] != b[2]) return a[2] > b[2] ? 1 : -1; 76 | if (a[1] != b[1]) return a[1] > b[1] ? 1 : -1; 77 | if (a[0] != b[0]) return a[0] > b[0] ? 1 : -1; 78 | return 0; 79 | } 80 | 81 | void fe_from_hex(fe r, const char *hex) { 82 | // load dynamic length hex string into 256bit integer (from right to left) 83 | fe_set64(r, 0); 84 | 85 | int cnt = 0, len = strlen(hex); 86 | while (len-- > 0) { 87 | u64 v = tolower(hex[len]); 88 | if (v >= '0' && v <= '9') v = v - '0'; 89 | else if (v >= 'a' && v <= 'f') v = v - 'a' + 10; 90 | else continue; 91 | 92 | r[cnt / 16] = (v << (cnt * 4 % 64)) | r[cnt / 16]; 93 | cnt += 1; 94 | } 95 | } 96 | 97 | INLINE void fe_shiftl(fe r, const u8 n) { 98 | if (n == 0) return; 99 | 100 | u8 s = n / 64; 101 | u8 rem = n % 64; 102 | 103 | for (int i = 3; i >= 0; --i) r[i] = i >= s ? r[i - s] : 0; 104 | if (rem == 0) return; 105 | 106 | u128 carry = 0; 107 | for (int i = 0; i < 4; ++i) { 108 | // todo: use umul128 109 | u128 val = ((u128)r[i]) << rem; 110 | r[i] = (u64)(val | carry); 111 | carry = val >> 64; 112 | } 113 | } 114 | 115 | INLINE void fe_shiftr64(fe r, const u8 n) { 116 | assert(n < 64); 117 | r[0] = (r[0] >> n) | (r[1] << (64 - n)); 118 | r[1] = (r[1] >> n) | (r[2] << (64 - n)); 119 | r[2] = (r[2] >> n) | (r[3] << (64 - n)); 120 | r[3] = (r[3] >> n); 121 | } 122 | 123 | // MARK: 320bit helpers 124 | 125 | void fe_mul_scalar(fe320 r, const fe a, const u64 b) { // 256bit * 64bit -> 320bit 126 | u64 h1, h2, c = 0; 127 | r[0] = umul128(a[0], b, &h1); 128 | r[1] = addc64(umul128(a[1], b, &h2), h1, c, &c); 129 | r[2] = addc64(umul128(a[2], b, &h1), h2, c, &c); 130 | r[3] = addc64(umul128(a[3], b, &h2), h1, c, &c); 131 | r[4] = addc64(0, h2, c, &c); 132 | } 133 | 134 | u64 fe320_addc(fe320 r, const fe320 a, const fe320 b) { 135 | u64 c = 0; 136 | r[0] = addc64(a[0], b[0], c, &c); 137 | r[1] = addc64(a[1], b[1], c, &c); 138 | r[2] = addc64(a[2], b[2], c, &c); 139 | r[3] = addc64(a[3], b[3], c, &c); 140 | r[4] = addc64(a[4], b[4], c, &c); 141 | return c; 142 | } 143 | 144 | u64 fe320_subc(fe320 r, const fe320 a, const fe320 b) { 145 | u64 c = 0; 146 | r[0] = subc64(a[0], b[0], c, &c); 147 | r[1] = subc64(a[1], b[1], c, &c); 148 | r[2] = subc64(a[2], b[2], c, &c); 149 | r[3] = subc64(a[3], b[3], c, &c); 150 | r[4] = subc64(a[4], b[4], c, &c); 151 | return c; 152 | } 153 | 154 | void fe320_add_shift(fe320 r, const fe320 a, const fe320 b, u64 ch) { 155 | u64 c = 0; 156 | addc64(a[0], b[0], c, &c); // keep only carry 157 | r[0] = addc64(a[1], b[1], c, &c); 158 | r[1] = addc64(a[2], b[2], c, &c); 159 | r[2] = addc64(a[3], b[3], c, &c); 160 | r[3] = addc64(a[4], b[4], c, &c); 161 | r[4] = c + ch; 162 | } 163 | 164 | // MARK: Modulo N arithmetic 165 | 166 | void fe_modn_neg(fe r, const fe a) { // r = -a (mod N) 167 | u64 c = 0; 168 | r[0] = subc64(FE_N[0], a[0], c, &c); 169 | r[1] = subc64(FE_N[1], a[1], c, &c); 170 | r[2] = subc64(FE_N[2], a[2], c, &c); 171 | r[3] = subc64(FE_N[3], a[3], c, &c); 172 | } 173 | 174 | void fe_modn_add(fe r, const fe a, const fe b) { // r = a + b (mod N) 175 | u64 c = 0; 176 | r[0] = addc64(a[0], b[0], c, &c); 177 | r[1] = addc64(a[1], b[1], c, &c); 178 | r[2] = addc64(a[2], b[2], c, &c); 179 | r[3] = addc64(a[3], b[3], c, &c); 180 | 181 | if (c) { 182 | r[0] = subc64(r[0], FE_N[0], 0, &c); 183 | r[1] = subc64(r[1], FE_N[1], c, &c); 184 | r[2] = subc64(r[2], FE_N[2], c, &c); 185 | r[3] = subc64(r[3], FE_N[3], c, &c); 186 | } 187 | } 188 | 189 | void fe_modn_sub(fe r, const fe a, const fe b) { // r = a - b (mod N) 190 | u64 c = 0; 191 | r[0] = subc64(a[0], b[0], c, &c); 192 | r[1] = subc64(a[1], b[1], c, &c); 193 | r[2] = subc64(a[2], b[2], c, &c); 194 | r[3] = subc64(a[3], b[3], c, &c); 195 | 196 | if (c) { 197 | r[0] = addc64(r[0], FE_N[0], 0, &c); 198 | r[1] = addc64(r[1], FE_N[1], c, &c); 199 | r[2] = addc64(r[2], FE_N[2], c, &c); 200 | r[3] = addc64(r[3], FE_N[3], c, &c); 201 | } 202 | } 203 | 204 | // clang-format off 205 | GLOBAL fe320 _NN = {0xbfd25e8cd0364141, 0xbaaedce6af48a03b, 0xfffffffffffffffe, 0xffffffffffffffff, 0x0}; 206 | GLOBAL fe320 _R2 = {0x896cf21467d7d140, 0x741496c20e7cf878, 0xe697f5e45bcd07c6, 0x9d671cd581c69bc5, 0x0}; 207 | GLOBAL u64 _MM64o = 0x4b0dff665588b13f; // 64bits lsb negative inverse of secp256k1 order 208 | // clang-format on 209 | 210 | // https://github.com/albertobsd/keyhunt/blob/main/secp256k1/IntMod.cpp#L1109 211 | void fe_modn_mul(fe r, const fe a, const fe b) { 212 | fe320 t = {0}, pr = {0}, p = {0}, rr = {0}; 213 | u64 ml, c; 214 | 215 | fe_mul_scalar(pr, a, b[0]); // pr = a * b[0] 216 | ml = pr[0] * _MM64o; 217 | fe_mul_scalar(p, _NN, ml); // p = N * ML 218 | c = fe320_addc(pr, pr, p); // r = pr + p 219 | memcpy(t, pr + 1, 32); 220 | t[4] = c; 221 | 222 | for (int i = 1; i < 4; ++i) { 223 | fe_mul_scalar(pr, a, b[i]); // pr = a * b[i] 224 | ml = (pr[0] + t[0]) * _MM64o; 225 | fe_mul_scalar(p, _NN, ml); // p = N * ML 226 | c = fe320_addc(pr, pr, p); // r = pr + p 227 | fe320_add_shift(t, t, pr, c); 228 | } 229 | 230 | fe320_subc(p, t, _NN); // p = t - N 231 | (int64_t)p[4] >= 0 ? memcpy(rr, p, 40) : memcpy(rr, t, 40); 232 | 233 | // normalize 234 | fe_mul_scalar(pr, _R2, rr[0]); // pr = rr * R2 235 | ml = pr[0] * _MM64o; 236 | fe_mul_scalar(p, _NN, ml); // p = N * ML 237 | c = fe320_addc(pr, pr, p); // r = pr + p 238 | memcpy(t, pr + 1, 32); 239 | t[4] = c; 240 | 241 | for (int i = 1; i < 4; ++i) { 242 | fe_mul_scalar(pr, _R2, rr[i]); // pr = rr * R2 243 | ml = (pr[0] + t[0]) * _MM64o; 244 | fe_mul_scalar(p, _NN, ml); // p = N * ML 245 | c = fe320_addc(pr, pr, p); // r = pr + p 246 | fe320_add_shift(t, t, pr, c); 247 | } 248 | 249 | fe320_subc(p, t, _NN); // p = t - N 250 | (int64_t)p[4] >= 0 ? memcpy(rr, p, 40) : memcpy(rr, t, 40); 251 | 252 | fe_clone(r, rr); 253 | } 254 | 255 | void fe_modn_add_stride(fe r, const fe base, const fe stride, const u64 offset) { 256 | fe t; // in case r and base are same pointer 257 | fe_set64(t, offset); 258 | fe_modn_mul(t, t, stride); // r = offset * stride 259 | fe_modn_add(r, t, base); // r = offset * stride + base 260 | } 261 | 262 | void fe_modn_from_hex(fe r, const char *hex) { 263 | fe_from_hex(r, hex); 264 | if (fe_cmp(r, FE_N) >= 0) fe_modn_sub(r, r, FE_N); 265 | } 266 | 267 | // MARK: Modulo P arithmetic 268 | 269 | void fe_modp_neg(fe r, const fe a) { // r = -a (mod P) 270 | u64 c = 0; 271 | r[0] = subc64(FE_P[0], a[0], c, &c); 272 | r[1] = subc64(FE_P[1], a[1], c, &c); 273 | r[2] = subc64(FE_P[2], a[2], c, &c); 274 | r[3] = subc64(FE_P[3], a[3], c, &c); 275 | } 276 | 277 | void fe_modp_sub(fe r, const fe a, const fe b) { // r = a - b (mod P) 278 | u64 c = 0; 279 | r[0] = subc64(a[0], b[0], c, &c); 280 | r[1] = subc64(a[1], b[1], c, &c); 281 | r[2] = subc64(a[2], b[2], c, &c); 282 | r[3] = subc64(a[3], b[3], c, &c); 283 | 284 | if (c) { 285 | r[0] = addc64(r[0], FE_P[0], 0, &c); 286 | r[1] = addc64(r[1], FE_P[1], c, &c); 287 | r[2] = addc64(r[2], FE_P[2], c, &c); 288 | r[3] = addc64(r[3], FE_P[3], c, &c); 289 | } 290 | } 291 | 292 | void fe_modp_add(fe r, const fe a, const fe b) { // r = a + b (mod P) 293 | u64 c = 0; 294 | r[0] = addc64(a[0], b[0], c, &c); 295 | r[1] = addc64(a[1], b[1], c, &c); 296 | r[2] = addc64(a[2], b[2], c, &c); 297 | r[3] = addc64(a[3], b[3], c, &c); 298 | 299 | if (c) { 300 | r[0] = subc64(r[0], FE_P[0], 0, &c); 301 | r[1] = subc64(r[1], FE_P[1], c, &c); 302 | r[2] = subc64(r[2], FE_P[2], c, &c); 303 | r[3] = subc64(r[3], FE_P[3], c, &c); 304 | } 305 | } 306 | 307 | void fe_modp_mul(fe r, const fe a, const fe b) { 308 | u64 rr[8] = {0}, tt[5] = {0}, c = 0; 309 | 310 | // 256bit * 256bit -> 512bit 311 | fe_mul_scalar(rr, a, b[0]); 312 | fe_mul_scalar(tt, a, b[1]); 313 | rr[1] = addc64(rr[1], tt[0], c, &c); 314 | rr[2] = addc64(rr[2], tt[1], c, &c); 315 | rr[3] = addc64(rr[3], tt[2], c, &c); 316 | rr[4] = addc64(rr[4], tt[3], c, &c); 317 | rr[5] = addc64(rr[5], tt[4], c, &c); 318 | fe_mul_scalar(tt, a, b[2]); 319 | rr[2] = addc64(rr[2], tt[0], c, &c); 320 | rr[3] = addc64(rr[3], tt[1], c, &c); 321 | rr[4] = addc64(rr[4], tt[2], c, &c); 322 | rr[5] = addc64(rr[5], tt[3], c, &c); 323 | rr[6] = addc64(rr[6], tt[4], c, &c); 324 | fe_mul_scalar(tt, a, b[3]); 325 | rr[3] = addc64(rr[3], tt[0], c, &c); 326 | rr[4] = addc64(rr[4], tt[1], c, &c); 327 | rr[5] = addc64(rr[5], tt[2], c, &c); 328 | rr[6] = addc64(rr[6], tt[3], c, &c); 329 | rr[7] = addc64(rr[7], tt[4], c, &c); 330 | 331 | // reduce 512bit -> 320bit 332 | fe_mul_scalar(tt, rr + 4, 0x1000003D1ULL); 333 | rr[0] = addc64(rr[0], tt[0], 0, &c); 334 | rr[1] = addc64(rr[1], tt[1], c, &c); 335 | rr[2] = addc64(rr[2], tt[2], c, &c); 336 | rr[3] = addc64(rr[3], tt[3], c, &c); 337 | 338 | // reduce 320bit -> 256bit 339 | u64 hi, lo; 340 | lo = umul128(tt[4] + c, 0x1000003D1ULL, &hi); 341 | r[0] = addc64(rr[0], lo, 0, &c); 342 | r[1] = addc64(rr[1], hi, c, &c); 343 | r[2] = addc64(rr[2], 0, c, &c); 344 | r[3] = addc64(rr[3], 0, c, &c); 345 | 346 | if (fe_cmp(r, FE_P) >= 0) fe_modp_sub(r, r, FE_P); 347 | } 348 | 349 | void fe_modp_sqr(fe r, const fe a) { 350 | // from: https://github.com/JeanLucPons/VanitySearch/blob/1.19/IntMod.cpp#L1034 351 | // ~8% faster than fe_modmul(r, a, a) 352 | // return fe_modmul(r, a, a); 353 | 354 | // k=0 355 | u64 rr[8] = {0}, tt[5] = {0}, c = 0, t1, t2, lo, hi; 356 | rr[0] = umul128(a[0], a[0], &tt[1]); 357 | 358 | // k=1 359 | tt[3] = umul128(a[0], a[1], &tt[4]); 360 | tt[3] = addc64(tt[3], tt[3], 0, &c); 361 | tt[4] = addc64(tt[4], tt[4], c, &c); 362 | t1 = c; 363 | tt[3] = addc64(tt[1], tt[3], 0, &c); 364 | tt[4] = addc64(tt[4], 0, c, &c); 365 | t1 += c; 366 | rr[1] = tt[3]; 367 | 368 | // k=2 369 | tt[0] = umul128(a[0], a[2], &tt[1]); 370 | tt[0] = addc64(tt[0], tt[0], 0, &c); 371 | tt[1] = addc64(tt[1], tt[1], c, &c); 372 | t2 = c; 373 | lo = umul128(a[1], a[1], &hi); 374 | tt[0] = addc64(tt[0], lo, 0, &c); 375 | tt[1] = addc64(tt[1], hi, c, &c); 376 | t2 += c; 377 | tt[0] = addc64(tt[0], tt[4], 0, &c); 378 | tt[1] = addc64(tt[1], t1, c, &c); 379 | t2 += c; 380 | rr[2] = tt[0]; 381 | 382 | // k=3 383 | tt[3] = umul128(a[0], a[3], &tt[4]); 384 | lo = umul128(a[1], a[2], &hi); 385 | tt[3] = addc64(tt[3], lo, 0, &c); 386 | tt[4] = addc64(tt[4], hi, c, &c); 387 | t1 = c + c; 388 | tt[3] = addc64(tt[3], tt[3], 0, &c); 389 | tt[4] = addc64(tt[4], tt[4], c, &c); 390 | t1 += c; 391 | tt[3] = addc64(tt[1], tt[3], 0, &c); 392 | tt[4] = addc64(tt[4], t2, c, &c); 393 | t1 += c; 394 | rr[3] = tt[3]; 395 | 396 | // k=4 397 | tt[0] = umul128(a[1], a[3], &tt[1]); 398 | tt[0] = addc64(tt[0], tt[0], 0, &c); 399 | tt[1] = addc64(tt[1], tt[1], c, &c); 400 | t2 = c; 401 | lo = umul128(a[2], a[2], &hi); 402 | tt[0] = addc64(tt[0], lo, 0, &c); 403 | tt[1] = addc64(tt[1], hi, c, &c); 404 | t2 += c; 405 | tt[0] = addc64(tt[0], tt[4], 0, &c); 406 | tt[1] = addc64(tt[1], t1, c, &c); 407 | t2 += c; 408 | rr[4] = tt[0]; 409 | 410 | // k=5 411 | tt[3] = umul128(a[2], a[3], &tt[4]); 412 | tt[3] = addc64(tt[3], tt[3], 0, &c); 413 | tt[4] = addc64(tt[4], tt[4], c, &c); 414 | t1 = c; 415 | tt[3] = addc64(tt[3], tt[1], 0, &c); 416 | tt[4] = addc64(tt[4], t2, c, &c); 417 | t1 += c; 418 | rr[5] = tt[3]; 419 | 420 | // k=6 421 | tt[0] = umul128(a[3], a[3], &tt[1]); 422 | tt[0] = addc64(tt[0], tt[4], 0, &c); 423 | tt[1] = addc64(tt[1], t1, c, &c); 424 | rr[6] = tt[0]; 425 | 426 | // k=7 427 | rr[7] = tt[1]; 428 | 429 | // reduce 512bit -> 320bit 430 | fe_mul_scalar(tt, rr + 4, 0x1000003D1ULL); 431 | rr[0] = addc64(rr[0], tt[0], 0, &c); 432 | rr[1] = addc64(rr[1], tt[1], c, &c); 433 | rr[2] = addc64(rr[2], tt[2], c, &c); 434 | rr[3] = addc64(rr[3], tt[3], c, &c); 435 | 436 | // reduce 320bit -> 256bit 437 | lo = umul128(tt[4] + c, 0x1000003D1ULL, &hi); 438 | r[0] = addc64(rr[0], lo, 0, &c); 439 | r[1] = addc64(rr[1], hi, c, &c); 440 | r[2] = addc64(rr[2], 0, c, &c); 441 | r[3] = addc64(rr[3], 0, c, &c); 442 | 443 | if (fe_cmp(r, FE_P) >= 0) fe_modp_sub(r, r, FE_P); 444 | } 445 | 446 | void _fe_modp_inv_binpow(fe r, const fe a) { 447 | // a^(P-2) = a^-1 (mod P) 448 | // https://e-maxx.ru/algo/reverse_element https://e-maxx.ru/algo/binary_pow 449 | fe q = {1}, p, t; 450 | fe_clone(p, FE_P); 451 | fe_clone(t, a); 452 | p[0] -= 2; 453 | 454 | while (p[0] || p[1] || p[2] || p[3]) { // p > 0 455 | if ((a[0] & 1) != 0) fe_modp_mul(q, q, t); // when odd 456 | fe_modp_sqr(t, t); 457 | fe_shiftr64(p, 1); 458 | } 459 | 460 | fe_clone(r, q); 461 | } 462 | 463 | void _fe_modp_inv_addchn(fe r, const fe a) { 464 | // https://briansmith.org/ecc-inversion-addition-chains-01#secp256k1_field_inversion 465 | fe x2, x3, x6, x9, x11, x22, x44, x88, x176, x220, x223, t1; 466 | fe_modp_sqr(x2, a); 467 | fe_modp_mul(x2, x2, a); 468 | 469 | fe_clone(x3, x2); 470 | fe_modp_sqr(x3, x2); 471 | fe_modp_mul(x3, x3, a); 472 | 473 | fe_clone(x6, x3); 474 | for (int j = 0; j < 3; j++) fe_modp_sqr(x6, x6); 475 | fe_modp_mul(x6, x6, x3); 476 | 477 | fe_clone(x9, x6); 478 | for (int j = 0; j < 3; j++) fe_modp_sqr(x9, x9); 479 | fe_modp_mul(x9, x9, x3); 480 | 481 | fe_clone(x11, x9); 482 | for (int j = 0; j < 2; j++) fe_modp_sqr(x11, x11); 483 | fe_modp_mul(x11, x11, x2); 484 | 485 | fe_clone(x22, x11); 486 | for (int j = 0; j < 11; j++) fe_modp_sqr(x22, x22); 487 | fe_modp_mul(x22, x22, x11); 488 | 489 | fe_clone(x44, x22); 490 | for (int j = 0; j < 22; j++) fe_modp_sqr(x44, x44); 491 | fe_modp_mul(x44, x44, x22); 492 | 493 | fe_clone(x88, x44); 494 | for (int j = 0; j < 44; j++) fe_modp_sqr(x88, x88); 495 | fe_modp_mul(x88, x88, x44); 496 | 497 | fe_clone(x176, x88); 498 | for (int j = 0; j < 88; j++) fe_modp_sqr(x176, x176); 499 | fe_modp_mul(x176, x176, x88); 500 | 501 | fe_clone(x220, x176); 502 | for (int j = 0; j < 44; j++) fe_modp_sqr(x220, x220); 503 | fe_modp_mul(x220, x220, x44); 504 | 505 | fe_clone(x223, x220); 506 | for (int j = 0; j < 3; j++) fe_modp_sqr(x223, x223); 507 | fe_modp_mul(x223, x223, x3); 508 | 509 | fe_clone(t1, x223); 510 | for (int j = 0; j < 23; j++) fe_modp_sqr(t1, t1); 511 | fe_modp_mul(t1, t1, x22); 512 | for (int j = 0; j < 5; j++) fe_modp_sqr(t1, t1); 513 | fe_modp_mul(t1, t1, a); 514 | for (int j = 0; j < 3; j++) fe_modp_sqr(t1, t1); 515 | fe_modp_mul(t1, t1, x2); 516 | for (int j = 0; j < 2; j++) fe_modp_sqr(t1, t1); 517 | fe_modp_mul(r, t1, a); 518 | } 519 | 520 | INLINE void fe_modp_inv(fe r, const fe a) { return _fe_modp_inv_addchn(r, a); } 521 | 522 | void fe_modp_grpinv(fe r[], const u32 n) { 523 | fe *zs = (fe *)malloc(n * sizeof(fe)); 524 | 525 | fe_clone(*zs, r[0]); 526 | for (u32 i = 1; i < n; ++i) fe_modp_mul(*(zs + i), *(zs + (i - 1)), r[i]); 527 | 528 | fe t1, t2; 529 | fe_clone(t1, *(zs + (n - 1))); 530 | fe_modp_inv(t1, t1); 531 | 532 | for (u32 i = n - 1; i > 0; --i) { 533 | fe_modp_mul(t2, t1, *(zs + (i - 1))); 534 | fe_modp_mul(t1, r[i], t1); 535 | fe_clone(r[i], t2); 536 | } 537 | 538 | fe_clone(r[0], t1); 539 | free(zs); 540 | } 541 | 542 | // MARK: EC Point 543 | // https://eprint.iacr.org/2015/1060.pdf 544 | // https://hyperelliptic.org/EFD/g1p/auto-shortw.html 545 | 546 | typedef struct pe { 547 | fe x, y, z; 548 | } pe; 549 | 550 | GLOBAL pe G1 = { 551 | .x = {0x59f2815b16f81798, 0x029bfcdb2dce28d9, 0x55a06295ce870b07, 0x79be667ef9dcbbac}, 552 | .y = {0x9c47d08ffb10d4b8, 0xfd17b448a6855419, 0x5da4fbfc0e1108a8, 0x483ada7726a3c465}, 553 | .z = {0x1, 0x0, 0x0, 0x0}, 554 | }; 555 | 556 | GLOBAL pe G2 = { 557 | .x = {0xabac09b95c709ee5, 0x5c778e4b8cef3ca7, 0x3045406e95c07cd8, 0xc6047f9441ed7d6d}, 558 | .y = {0x236431a950cfe52a, 0xf7f632653266d0e1, 0xa3c58419466ceaee, 0x1ae168fea63dc339}, 559 | .z = {0x1, 0x0, 0x0, 0x0}, 560 | }; 561 | 562 | INLINE void pe_clone(pe *r, const pe *a) { 563 | memcpy(r, a, sizeof(pe)); 564 | // fe_clone(r->x, a->x); 565 | // fe_clone(r->y, a->y); 566 | // fe_clone(r->z, a->z); 567 | } 568 | 569 | // https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Affine_Coordinates 570 | 571 | void ec_affine_dbl(pe *r, const pe *p) { 572 | // λ = (3 * x^2) / (2 * y) 573 | // x = λ^2 - 2 * x 574 | // y = λ * (x1 - x) - y1 575 | fe t1, t2, t3; 576 | fe_modp_sqr(t1, p->x); // x^2 577 | fe_modp_add(t2, t1, t1); // 2 * x^2 578 | fe_modp_add(t2, t2, t1); // 3 * x^2 579 | fe_modp_add(t1, p->y, p->y); // 2 * y 580 | fe_modp_inv(t1, t1); // 581 | fe_modp_mul(t1, t2, t1); // λ = (3 * x^2) / (2 * y) 582 | fe_modp_sqr(t3, t1); // λ^2 583 | fe_modp_sub(t3, t3, p->x); // λ^2 - x1 584 | fe_modp_sub(t3, t3, p->x); // λ^2 - x1 - x1 585 | fe_modp_sub(t2, p->x, t3); // x1 - x 586 | fe_modp_mul(t2, t1, t2); // λ * (x1 - x) 587 | fe_modp_sub(r->y, t2, p->y); // λ * (x1 - x) - y1 588 | fe_clone(r->x, t3); 589 | } 590 | 591 | void ec_affine_add(pe *r, const pe *p, const pe *q) { 592 | // λ = (y1 - y2) / (x1 - x2) 593 | // x = λ^2 - x1 - x2 594 | // y = λ * (x1 - x) - y1 595 | fe t1, t2, t3, t4; 596 | fe_modp_sub(t1, p->y, q->y); // y1 - y2 597 | fe_modp_sub(t2, p->x, q->x); // x1 - x2 598 | fe_modp_inv(t2, t2); // 599 | fe_modp_mul(t1, t1, t2); // λ = (y1 - y2) / (x1 - x2) 600 | fe_modp_sqr(t3, t1); // λ^2 601 | fe_modp_sub(t3, t3, p->x); // λ^2 - x1 602 | fe_modp_sub(t3, t3, q->x); // λ^2 - x1 - x2 603 | fe_modp_sub(t4, p->x, t3); // x1 - x 604 | fe_modp_mul(t4, t1, t4); // λ * (x1 - x) 605 | fe_modp_sub(r->y, t4, p->y); // λ * (x1 - x) - y1 606 | fe_clone(r->x, t3); 607 | } 608 | 609 | // https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Standard_Projective_Coordinates 610 | 611 | void _ec_jacobi_dbl1(pe *r, const pe *p) { 612 | // W = a*Z^2 + 3*X^2 613 | // S = Y*Z 614 | // B = X*Y*S 615 | // H = W^2 - 8*B 616 | // X' = 2*H*S 617 | // Y' = W*(4*B - H) - 8*Y^2*S^2 618 | // Z' = 8*S^3 619 | fe w, s, b, h, t; 620 | fe_modp_sqr(t, p->x); // X^2 621 | fe_modp_add(w, t, t); // 2*X^2 622 | fe_modp_add(w, w, t); // 3*X^2 623 | fe_modp_mul(s, p->y, p->z); // Y*Z 624 | fe_modp_mul(b, p->x, p->y); // X*Y 625 | fe_modp_mul(b, b, s); // X*Y*S 626 | fe_modp_add(b, b, b); // 2*B 627 | fe_modp_add(b, b, b); // 4*B 628 | fe_modp_add(t, b, b); // 8*B 629 | fe_modp_sqr(h, w); // W^2 630 | fe_modp_sub(h, h, t); // W^2 - 8*B 631 | fe_modp_mul(r->x, h, s); // H*S 632 | fe_modp_add(r->x, r->x, r->x); // 2*H*S 633 | fe_modp_sub(t, b, h); // 4*B - H 634 | fe_modp_mul(t, w, t); // W*(4*B - H) 635 | fe_modp_sqr(r->y, p->y); // Y^2 636 | fe_modp_sqr(h, s); // S^2 637 | fe_modp_mul(r->y, r->y, h); // Y^2*S^2 638 | fe_modp_add(r->y, r->y, r->y); // 2*Y^2*S^2 639 | fe_modp_add(r->y, r->y, r->y); // 4*Y^2*S^2 640 | fe_modp_add(r->y, r->y, r->y); // 8*Y^2*S^2 641 | fe_modp_sub(r->y, t, r->y); // W*(4*B - H) - 8*Y^2*S^2 642 | fe_modp_mul(r->z, h, s); // S^3 643 | fe_modp_add(r->z, r->z, r->z); // 2*S^3 644 | fe_modp_add(r->z, r->z, r->z); // 4*S^3 645 | fe_modp_add(r->z, r->z, r->z); // 8*S^3 646 | } 647 | 648 | void _ec_jacobi_add1(pe *r, const pe *p, const pe *q) { 649 | // u1 = qy * pz 650 | // u2 = py * qz 651 | // v1 = qx * pz 652 | // v2 = px * qz 653 | // if (v1 == v2) return u1 != u2 ? POINT_AT_INFINITY : POINT_DOUBLE(px, py, pz) 654 | // u = u1 - u2 655 | // v = v1 - v2 656 | // w = pz * qz 657 | // a = u^2 * w - v^3 - 2 * v^2 * v2 658 | // x3 = v * a 659 | // y3 = u * (v^2 * v2 - a) - v^3 * u2 660 | // z3 = v^3 * w 661 | fe u2, v2, u, v, w, a, vs, vc; 662 | fe_modp_mul(u2, p->y, q->z); // u2 = py * qz 663 | fe_modp_mul(v2, p->x, q->z); // v2 = px * qz 664 | fe_modp_mul(u, q->y, p->z); // u1 = qy * pz 665 | fe_modp_mul(v, q->x, p->z); // v1 = qx * pz 666 | assert(fe_cmp(v, v2) != 0); // if (v1 == v2) return 667 | fe_modp_mul(w, p->z, q->z); // w = pz * qz 668 | fe_modp_sub(u, u, u2); // u = u1 - u2 669 | fe_modp_sub(v, v, v2); // v = v1 - v2 670 | fe_modp_sqr(vs, v); // v^2 671 | fe_modp_mul(vc, vs, v); // v^3 672 | fe_modp_mul(vs, vs, v2); // v^2 * v2 673 | fe_modp_mul(r->z, vc, w); // z3 = v^3 * w 674 | fe_modp_sqr(a, u); // u^2 675 | fe_modp_mul(a, a, w); // u^2 * w 676 | fe_modp_add(w, vs, vs); // 2 * v^2 * v2 [w reused] 677 | fe_modp_sub(a, a, vc); // u^2 * w - v^3 678 | fe_modp_sub(a, a, w); // u^2 * w - v^3 - 2 * v^2 * v2 679 | fe_modp_mul(r->x, v, a); // x3 = v * a 680 | fe_modp_sub(a, vs, a); // v^2 * v2 - a [a reused] 681 | fe_modp_mul(a, a, u); // u * (v^2 * v2 - a) [a reused] 682 | fe_modp_mul(u, vc, u2); // v^3 * u2 [u reused] 683 | fe_modp_sub(r->y, a, u); // y3 = u * (v^2 * v2 - a) - v^3 * u2 684 | } 685 | 686 | void _ec_jacobi_rdc1(pe *r, const pe *a) { 687 | // reduce Standard Projective to Affine 688 | fe_clone(r->z, a->z); 689 | fe_modp_inv(r->z, r->z); 690 | fe_modp_mul(r->x, a->x, r->z); 691 | fe_modp_mul(r->y, a->y, r->z); 692 | fe_set64(r->z, 0x1); 693 | } 694 | 695 | void _ec_jacobi_grprdc1(pe r[], u64 n) { 696 | fe *zz = (fe *)malloc(n * sizeof(fe)); 697 | for (u64 i = 0; i < n; ++i) fe_clone(zz[i], r[i].z); 698 | fe_modp_grpinv(zz, n); 699 | 700 | for (u64 i = 0; i < n; ++i) { 701 | fe_modp_mul(r[i].x, r[i].x, zz[i]); 702 | fe_modp_mul(r[i].y, r[i].y, zz[i]); 703 | fe_set64(r[i].z, 0x1); 704 | } 705 | 706 | free(zz); 707 | } 708 | 709 | // https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates 710 | 711 | void _ec_jacobi_dbl2(pe *r, const pe *p) { 712 | // if (Y == 0) return POINT_AT_INFINITY 713 | // S = 4*X*Y^2 714 | // M = 3*X^2 + a*Z^4 715 | // X' = M^2 - 2*S 716 | // Y' = M*(S - X') - 8*Y^4 717 | // Z' = 2*Y*Z 718 | u64 s[4], m[4], t[4]; 719 | fe_modp_mul(r->z, p->y, p->z); // Y*Z 720 | fe_modp_add(r->z, r->z, r->z); // 2*Y*Z 721 | fe_modp_sqr(t, p->y); // Y^2 722 | fe_modp_mul(s, p->x, t); // X*Y^2 723 | fe_modp_add(s, s, s); // 2*X*Y^2 724 | fe_modp_add(s, s, s); // 4*X*Y^2 725 | fe_modp_sqr(t, t); // Y^4 726 | fe_modp_add(r->y, t, t); // 2*Y^4 727 | fe_modp_add(t, r->y, r->y); // 4*Y^4 728 | fe_modp_add(r->y, t, t); // 8*Y^4 729 | fe_modp_sqr(t, p->x); // X^2 730 | fe_modp_add(m, t, t); // 2*X^2 731 | fe_modp_add(m, m, t); // 3*X^2 732 | fe_modp_sqr(r->x, m); // M^2 733 | fe_modp_add(t, s, s); // 2*S 734 | fe_modp_sub(r->x, r->x, t); // M^2 - 2*S 735 | fe_modp_sub(t, s, r->x); // S - X' 736 | fe_modp_mul(t, m, t); // M*(S - X') 737 | fe_modp_sub(r->y, t, r->y); // M*(S - X') - 8*Y^4 738 | } 739 | 740 | void _ec_jacobi_add2(pe *r, const pe *p, const pe *q) { 741 | // U1 = px * qz ** 2 742 | // S1 = py * qz ** 3 743 | // U2 = qx * pz ** 2 744 | // S2 = qy * pz ** 3 745 | // if (U1 == U2) return S1 != S2 ? (0, 0, 1) : jacobian_double(p) 746 | // H = U2 - U1 747 | // R = S2 - S1 748 | // U1H2 = U1 * H ** 2 749 | // nx = R ** 2 - H ** 3 - 2 * U1H2 750 | // ny = R * (U1H2 - nx) - S1 * H ** 3 751 | // nz = H * pz * qz 752 | fe u1, u2, s1, s2, tt, ta; 753 | fe_modp_sqr(tt, q->z); // qz ** 2 754 | fe_modp_mul(u1, p->x, tt); // U1 = px * qz ** 2 755 | fe_modp_mul(ta, tt, q->z); // qz ** 3 756 | fe_modp_mul(s1, p->y, ta); // S1 = py * qz ** 3 757 | fe_modp_sqr(tt, p->z); // pz ** 2 758 | fe_modp_mul(u2, q->x, tt); // U2 = qx * pz ** 2 759 | assert(fe_cmp(u1, u2) != 0); // if (U1 == U2) return 760 | fe_modp_mul(ta, tt, p->z); // pz ** 3 761 | fe_modp_mul(s2, q->y, ta); // S2 = qy * pz ** 3 762 | fe_modp_sub(u2, u2, u1); // H = U2 - U1 [u2 reused] 763 | fe_modp_sub(s2, s2, s1); // R = S2 - S1 [s2 reused] 764 | fe_modp_sqr(tt, u2); // H ** 2 765 | fe_modp_mul(u1, u1, tt); // U1H2 = U1 * H ** 2 [s1 reused] 766 | fe_modp_mul(tt, tt, u2); // H ** 3 767 | fe_modp_add(ta, u1, u1); // 2 * U1H2 768 | fe_modp_sqr(r->x, s2); // R ** 2 769 | fe_modp_sub(r->x, r->x, tt); // R ** 2 - H ** 3 770 | fe_modp_sub(r->x, r->x, ta); // nx = R ** 2 - H ** 3 - 2 * U1H2 771 | fe_modp_mul(ta, tt, s1); // S1 * H ** 3 772 | fe_modp_sub(r->y, u1, r->x); // U1H2 - nx 773 | fe_modp_mul(r->y, r->y, s2); // R * (U1H2 - nx) 774 | fe_modp_sub(r->y, r->y, ta); // R * (U1H2 - nx) - S1 * H ** 3 775 | fe_modp_mul(r->z, p->z, q->z); // pz * qz 776 | fe_modp_mul(r->z, r->z, u2); // H * pz * qz 777 | } 778 | 779 | void _ec_jacobi_rdc2(pe *r, const pe *a) { 780 | // reduce Jacobian to Affine 781 | fe t; 782 | fe_clone(r->z, a->z); 783 | fe_modp_inv(r->z, r->z); 784 | fe_modp_sqr(t, r->z); // z ** 2 785 | fe_modp_mul(r->x, a->x, t); // x = x * z ** 2 786 | fe_modp_mul(t, t, r->z); // z ** 3 787 | fe_modp_mul(r->y, a->y, t); // y = y * z ** 3 788 | fe_set64(r->z, 0x1); 789 | } 790 | 791 | void _ec_jacobi_grprdc2(pe r[], u64 n) { 792 | fe *zz = (fe *)malloc(n * sizeof(fe)); 793 | for (u64 i = 0; i < n; ++i) fe_clone(zz[i], r[i].z); 794 | fe_modp_grpinv(zz, n); 795 | 796 | fe z = {0}; 797 | for (u64 i = 0; i < n; ++i) { 798 | fe_modp_sqr(z, zz[i]); // z^2 799 | fe_modp_mul(r[i].x, r[i].x, z); // x = x * z^2 800 | fe_modp_mul(z, z, zz[i]); // z^3 801 | fe_modp_mul(r[i].y, r[i].y, z); // y = y * z^3 802 | fe_set64(r[i].z, 0x1); 803 | } 804 | 805 | free(zz); 806 | } 807 | 808 | // v1. add: ~6.6M it/s, dbl: ~5.6M it/s 809 | // v2. add: ~5.4M it/s, dbl: ~7.8M it/s 810 | // v1 is used because add operation is more frequent 811 | 812 | INLINE void ec_jacobi_dbl(pe *r, const pe *p) { return _ec_jacobi_dbl1(r, p); } 813 | INLINE void ec_jacobi_add(pe *r, const pe *p, const pe *q) { return _ec_jacobi_add1(r, p, q); } 814 | INLINE void ec_jacobi_rdc(pe *r, const pe *a) { return _ec_jacobi_rdc1(r, a); } 815 | INLINE void ec_jacobi_grprdc(pe r[], u64 n) { return _ec_jacobi_grprdc1(r, n); } 816 | // INLINE void ec_jacobi_dbl(pe *r, const pe *p) { return _ec_jacobi_dbl2(r, p); } 817 | // INLINE void ec_jacobi_add(pe *r, const pe *p, const pe *q) { return _ec_jacobi_add2(r, p, q); } 818 | // INLINE void ec_jacobi_rdc(pe *r, const pe *a) { return _ec_jacobi_rdc2(r, a); } 819 | // INLINE void ec_jacobi_grprdc(pe r[], u64 n) { return _ec_jacobi_grprdc2(r, n); } 820 | 821 | void ec_jacobi_mul(pe *r, const pe *p, const fe k) { 822 | // double-and-add in Jacobian space 823 | pe t; 824 | pe_clone(&t, p); 825 | fe_set64(r->x, 0x0); // todo: for first iteration to avoid point of infinity 826 | fe_set64(r->y, 0x0); 827 | fe_set64(r->z, 0x1); 828 | 829 | u32 bits = fe_bitlen(k); 830 | for (u32 i = 0; i < bits; ++i) { 831 | if (k[i / 64] & (1ULL << (i % 64))) { 832 | // todo: remove if condition / here simplified check to point of infinity 833 | if (r->x[0] == 0 && r->y[0] == 0) { 834 | // printf("add(0): %d ~ %d\n", i, i / 64); 835 | pe_clone(r, &t); 836 | } else { 837 | // printf("add(1): %d ~ %d\n", i, i / 64); 838 | ec_jacobi_add(r, r, &t); 839 | } 840 | } 841 | ec_jacobi_dbl(&t, &t); 842 | } 843 | } 844 | 845 | INLINE void ec_jacobi_addrdc(pe *r, const pe *p, const pe *q) { 846 | ec_jacobi_add(r, p, q); 847 | ec_jacobi_rdc(r, r); 848 | } 849 | 850 | INLINE void ec_jacobi_mulrdc(pe *r, const pe *p, const fe k) { 851 | ec_jacobi_mul(r, p, k); 852 | ec_jacobi_rdc(r, r); 853 | } 854 | 855 | INLINE void ec_jacobi_dblrdc(pe *r, const pe *p) { 856 | ec_jacobi_dbl(r, p); 857 | ec_jacobi_rdc(r, r); 858 | } 859 | 860 | bool ec_verify(const pe *p) { 861 | // y^2 = x^3 + 7 862 | pe q, g; 863 | pe_clone(&q, p); 864 | ec_jacobi_rdc(&q, &q); 865 | 866 | pe_clone(&g, &q); 867 | fe_modp_sqr(g.y, g.y); // y^2 868 | fe_modp_sqr(g.x, g.x); // x^2 869 | fe_modp_mul(g.x, g.x, q.x); // x^3 870 | fe_modp_sub(g.y, g.y, g.x); // y^2 - x^3 871 | return g.y[0] == 7 && g.y[1] == 0 && g.y[2] == 0 && g.y[3] == 0; 872 | } 873 | 874 | // MARK: EC GTable 875 | 876 | u64 _GTABLE_W = 14; 877 | pe *_gtable = NULL; // GTable for precomputed points 878 | 879 | // https://www.sav.sk/journals/uploads/0215094304C459.pdf (Algorithm 3) 880 | size_t ec_gtable_init() { 881 | u64 n = 1 << _GTABLE_W; 882 | u64 d = ((256 - 1) / _GTABLE_W) + 1; 883 | u64 s = n * d - d; 884 | 885 | size_t mem_size = s * sizeof(pe); 886 | if (_gtable != NULL) free(_gtable); 887 | _gtable = (pe *)malloc(mem_size); 888 | 889 | pe b, p; 890 | pe_clone(&b, &G1); 891 | for (u64 i = 0; i < d; ++i) { 892 | u64 x = (n - 1) * i; 893 | pe_clone(&_gtable[x], &b); 894 | pe_clone(&p, &b); 895 | for (u64 j = 1; j < n - 1; ++j) { 896 | j == 1 ? ec_jacobi_dbl(&p, &p) : ec_jacobi_add(&p, &p, &b); 897 | x = (n - 1) * i + j; 898 | pe_clone(&_gtable[x], &p); 899 | } 900 | ec_jacobi_add(&b, &p, &b); 901 | } 902 | 903 | ec_jacobi_grprdc(_gtable, s); 904 | return mem_size; 905 | } 906 | 907 | void ec_gtable_mul(pe *r, const fe pk) { 908 | if (_gtable == NULL) { 909 | printf("GTable is not initialized\n"); 910 | exit(1); 911 | } 912 | 913 | u64 n = 1 << _GTABLE_W; 914 | u64 d = ((256 - 1) / _GTABLE_W) + 1; 915 | pe q = {0}; 916 | fe k; 917 | fe_clone(k, pk); 918 | 919 | for (u64 i = 0; i < d; ++i) { 920 | u64 b = k[0] & (n - 1); 921 | fe_shiftr64(k, _GTABLE_W); 922 | if (!b) continue; 923 | 924 | u64 x = (n - 1) * i + b - 1; 925 | fe_iszero(q.x) ? pe_clone(&q, &_gtable[x]) : ec_jacobi_add(&q, &q, &_gtable[x]); 926 | } 927 | 928 | pe_clone(r, &q); 929 | } 930 | -------------------------------------------------------------------------------- /data/btc-bw-hash: -------------------------------------------------------------------------------- 1 | # from: https://privatekeys.pw/brainwallet/bitcoin/1 2 | bf1c61ac19576d71d4623b185f3bae2a3d4df6bc 3 | ac9ea341afb74843f80205e8c8da0abc822fa5ec 4 | adf9c056760bd5cd90feac7ccac2310af3f163a5 5 | f750c55bea03af8a720c46b5d6edea93644cdaf7 6 | 882269c2463f7a74f8d778c498997377bf78a581 7 | 38900f86d127dd98129d8bab84f366b5f452a84d 8 | 2430a46fc45739929fce1cc96d7ed9ceeae2f875 9 | e7ce648c69fc5f24a61e4c60cdbb321d1b1ca453 10 | f218a48ad44bba44714c33874b124cf3cf05dc00 11 | b5bd079c4d57cc7fc28ecf8213a6b791625b8183 12 | 8f776aa76c2b7f7f67c79011c51cd90160636d77 13 | e5725de0a27617bdcf553e262e2c6640184c8641 14 | bb5cedcc3f55052591d6b9a77a45a7e88586f6a4 15 | c4c5d791fcb4654a1ef5e03fe0ad3d9c598f9827 16 | 7d6b6923578f7b6a76c7a973b7ecaf6650b08c71 17 | 2d6351944aa38af6aa46d4a74cbb9016cf19ee7e 18 | c5f864d6df25f9e36fece89a2171054d44123f57 19 | 087d32371bc97826891859b91b58bad515f3d356 20 | a2e27d39c2da9f3766da4cc666bf6866ecb98a21 21 | 8855445495f973348d9f7ce063e25e0d0ad9fdc0 22 | b28c4bddbb2d40670bcb0e2298fce7ee3a29bb2a 23 | d77522a2b18e0064aba02ca7f864a5bb22998259 24 | 2802952cbfcc89aa0f5e58137465d0eb0f6f86f1 25 | 29727c3ffba684d7f5ebadc85a3f5837ed88fad0 26 | 80676c61a90a122d765a3a112968dd2f244658e1 27 | 7a1965077a67bb981d8e25c5e87e44e02458641b 28 | aa0342d91027b35f07e0705eb1ca6e9f0f04e9a8 29 | fae71b3fec253b6db82f42b600eaa73e406996ee 30 | 9e786a76dfafaa8998186f03d6432a468fbed30f 31 | 0f24de03a313b4850c380d37f8b0f422c5d80dec 32 | 3c8c7cd6a40c414f682c7a7a3de790ef846e276f 33 | a8d7fe54cf3f9c974f9e2b5611e0d8160d518e34 34 | 23da1214adc848c2ca1a9d8afb56200d1a447536 35 | 2029758fa9d81f9c36f4be2ab8696ad10fc602f8 36 | 77114278616803e2691d8b7a2728a913cb119a51 37 | a96d5c113e33cf253a49fc11494466a8e2fd464b 38 | 321b9c44c8b30ec45616acb4658f61acc8e17e24 39 | c723fb48cea1f8558727babf311fbe9c718b7221 40 | 4844987fdb98dfafc46d219d7d0c62018021bdeb 41 | ed045637ca4117aace4c393be094246516917231 42 | c113849b90afe0e4ca905982e323c88c59d9aa54 43 | 581a452472eba03a1d270580347a397d0e6f4b19 44 | 211143a6df5bce411b6edc2ffd8fd0af7406b71c 45 | fa493ffbb8559fd829af92f36cd1827e8a7e1f6b 46 | 72461ff1cd8f7d233acaeee7ef522698ef579240 47 | 801314cd462b98c64dd4c3f4d6474cad11ea39d5 48 | 781894aadfddba9a6d21c8aa0e4cf27e831c87a8 49 | fcc23c6b4b132bf0aad9bc8d396101b8506693ad 50 | 0e6ad9a1ace444af423fb5a5706a6fc1a97dc23d 51 | aede75cb161ae63ee6cc20c21f49e922a1711c0c 52 | 778e6bd09a4be5d3477a93181a37d9a38581e260 53 | 5b9e5a4f7e8ee6b2849c96d1775d145f74c6b48f 54 | fd11ee92821ed444db36b27b7b8726cd3200f4c1 55 | 3f7510a41bf4ceb0529367fe55adfbb3f7e6c8f6 56 | 1f250be8fbb5d5fdb262cc72886a32a45b6064eb 57 | f2af58aa133a07c5e379a0b0427e1a838410e75f 58 | 1ef8f165a0c4d63e3e20ce1296df32564e32dcc7 59 | 9dc8f455d862113f06f70a54275fb0eccb5ef25b 60 | 962eadbd01d979f32fb9931e9e40168ad68ded7e 61 | b1cb57c65c21f79711b4a07d3609e1e4b330acc4 62 | 1ed5c8e342b3c52790c316bdf4d0554acd627abd 63 | af3bc231bb12f1ad10cf062386dbbbb26528cb22 64 | 8969a6ca69fdaea9e78c0064aa13718663b0f025 65 | a4e37e97d15c13bcba993cea62dc8b30f558c497 66 | aa507e51702416dae5ab5c1abe798f89c45c1bda 67 | 29497e931c1b8c7cdcf9460b709e9f42bcd3cce5 68 | cfeb063a217fb26c6f747dde6500beaa615270df 69 | 21bf98b5ad55abac1dd132fda780c64b3fcc2b1d 70 | 975046c9e65fca3801e78ccdef9632a3a775e685 71 | 6d3536a61d6308915bcc3b72476b7e40d27b08ce 72 | ecd43b4bca841f5e829c8cf4524792e9cdbab923 73 | d05b4dd41f26f2bae1f3e1ed25bdfb7a0161e1db 74 | d9a4412a86af50917c023ef2c790fd213437f65f 75 | 2cdd4dfe1c25077f63dff65303dee40de7d0b72a 76 | 8ced36d5f0af1e8c8cf80e1e9f4e76e8ce2379be 77 | 482bc0946efa74a5a3d005e693b2774e1aeb7dad 78 | a1523fa5cbc198ec96a51f7f2c1f81b9621d2003 79 | 522403e54eaf2956f586a76a47eb741abe497767 80 | fca21c1b93c81e5aa112456c497cdde35b4a7374 81 | ae626d970ca4ff03e2555c114cfff67fca84c337 82 | 7d4623f6c2025738975494d68d158b9e9f10fbae 83 | 3c90c984c0478db78f467e308541d0c8b8f7552b 84 | 7cdb4c83f019385b3c9eb4f2e99bf14da3753cd4 85 | 61fd695003def9ebf1b5c1c53e5225527aa2903c 86 | 3bf8582046e8e00fe43e817fde41881c9455a6c6 87 | 52d510a0b241d250d66fa180dfbb3cddf0ae53c4 88 | 431ffdbeeda7cf2c381a8d3a30d6f7cf301229d2 89 | 56658266357455cd66625cdb2f5d13cba43638ad 90 | 0067a95937f7a8205f47445d40ac6cd79fb0c954 91 | dafd1d1f2f527dbbc7db9d33f52949bb0f9a6dbe 92 | 7b1ce1dd5df61253f976373725ace5d3ec371337 93 | 3e546d0acc0de5aa3d66d7a920900ecbc66c2031 94 | 112d05f80c603cc701bfb55707ec3bb1af15dd6b 95 | 98900c1915f581b4059526d18a020ba4c81b2f7c 96 | 1773ab54ebe0fda812716540be9485af0432c2b6 97 | 35261a15590d90dce225e938cbc582d67f9ec109 98 | dcbf1d0e93aacdbd55773ff593c51f307c59d644 99 | 6b932d90bb48d09eb147e52487305b0b936afb5e 100 | 3b2129db835311d1ee3b5ccc32b1837352246318 101 | 371f197d5ba5e32bd98260eec7f0e51227b69690 102 | ed0846b53f458c72b85abe542fe51dbc2e91688a 103 | 146446c0a599956b8536c0f0aa6783f55d706859 104 | dfb5b3270ca730689f25c0dc658fbabd5e7eaeac 105 | 0c2f3de6e7a74cd83ae24b85ff0e504a0f04b524 106 | b4e497f002d276cc48c30bad8e51bbe01be912ba 107 | 2c1b606f18c4be63318bebccad58bde876168b5a 108 | 3535ce130c3e11e2f8887bfd1b462327f4e9837f 109 | a25b865343d3fcc08994005f496265b48349e363 110 | 989713b987912c270169c28df865407f516489a4 111 | 78be467620827fe8b772cbde9960d73c44743e31 112 | 1bfed6b568deb3a2a3876d04d563f2212e767b65 113 | 5b2217ae277596ed25bd73414cacc7b0038a4756 114 | 091a107374ffc6854910a469b96fe970674a8fa6 115 | 9bdeec59a912c90bc8cf4b8371e162bcff0dc0d3 116 | 328e165abcbd0e1ff12da00638b3cb86b3e6af15 117 | cffe3e032a686cc3f2c9e417865afa8a52ed962b 118 | a6287af29f21771b8c9b69cbc456f6ba36381034 119 | 7e38231d15425b95da73836e793a112b1785c62f 120 | 087f89c487733bd8aadb991e374d24e017f44116 121 | 5d14a857fce8da8edfb8f7d1c4bbc316622b7227 122 | e4c9ef528a2d477e8e073721103b322b7e31c67b 123 | 0a77fdb4cc81631b6ea2991ff60b47d57812d8e7 124 | eaf8bbd9e3d92233be76fd4c6c1df73846758764 125 | 0fb87a5071385b6976397d1c53ee16f09139a334 126 | acd34e86e91daaae680b344e4e94100749c75b8b 127 | 5f395a91d07712604d7cd6fabd685b9bfd3900dd 128 | 77bd7e0ac4f91482360bfaea3939aeb63c57ef27 129 | d959be6c92037995558f43a55b1c271628f96e8d 130 | 4cb84ea8c68f52e9182b9390a215ad1e98d8e671 131 | 105d54a4edcbe114a50bb01c79d230b7ed74a3e4 132 | 46eb7540f6e69e580f0496006be6d871aa7e5116 133 | 0337e0710056f114c9c469a68775498df9f9fa16 134 | d3e2013848229d02774ed46f4afc1df265f75029 135 | 54514fe9251b5e381d13171cd6fca63f61d8b726 136 | 12a7559d18cf13d0dbe754b8ae49b5cd0d6ee86c 137 | e49275e86ece605f271f26a9559520eb9c0ae8d8 138 | 1682a51f413157416149fe21c72887b35a72fe33 139 | b1ec1d5e0591abbbe3134c94c37e74d034b93122 140 | 7fe61869a0af6b914cd82093605e5176212a10e1 141 | 9c628c82aa7b81da7c6a235049eb2979c4a65cfc 142 | 967f8e8cb446218a1c75ec6354019befd82fa50f 143 | caf67cfe28b511498b0d1792bedeec6b6e8a3c8d 144 | fb54c1bc13b2212046f738e69b2a601e78a8669f 145 | dc3343b674cf2016b8968e6146ba5cc9228f14a4 146 | 85c295fc4582b05b4d7e38cd229f983731cd37d2 147 | 20ffdc5f39cd04089353bee51ce55dd594298708 148 | 3e4e5b16a396660d830a24911d406fd27c856f5d 149 | c5386f51a8a43556ba8e7744e6796db9682105ba 150 | 12d888821a57586aab8e620463bfb409986e6a25 151 | 65f2a3921afacd998e6c98a5aaa94325ed07ee14 152 | ed295ca4ef82006d88bfc211dc774ca6cc94ecf4 153 | e1d58d3cec49eaf6d3422bcbf1bbe8853d05f934 154 | 4df7afaf03073a5760c6c8b6feec31997eae47d1 155 | d7f7f6470ab932eac6940dbab8ec40eef99d2cf8 156 | fa07012264ede6d836cc344c3bb2a893001a11a9 157 | 0005736b486f87e5823909a89eb48dda185d3956 158 | bdc5d03c252b636b82c2a92f37818ff6b014e8e8 159 | dcc147afee4aa2d86b8e26b6bef80cd911c2401a 160 | 2b6fe909b99adbaf27f8cd95ad2fda6b4ecf790d 161 | 78d24d0dff5cf2fbae217bf40ffc460b39647569 162 | bb272955b95af2a1a8915438148e85c77cc0a2f4 163 | 53f6ef6df9675f7274691064b20f9401d21f5a24 164 | b1753ccbb78c3e5b1a17903632b20a5bd1f4e235 165 | a297bbfa23215a13f029b30a746bcfbe6d34d16d 166 | cdf9babfb6cb35e6ad167b7df09b3215a063bced 167 | 0cbb3eeba6c4805531b18a1d27957fba561f8689 168 | 0db73a29b81f42ba1cfc5610ee6f9ee4624c21c2 169 | b3994a50602a7f1e79a946120074e727c1dde5de 170 | 8d00a1ea801a48bbbbc5d99864c13b2f2328d8b8 171 | aa7d5e0e10c98627567c4faa8c66990db904d711 172 | 0b62de17ee840cc42866eec587fa2d9f2044e95c 173 | 106ec34cad3ef3ebb639ca9656af67ffea3a5e4f 174 | 7ac9efe0a2edffee3676bd62999a8e23d560853e 175 | 94eeb3e8b7bb1a61fc2ef4bdfcecae1729fd1d9e 176 | 85b38aa3c4a0494db698a7b7221d14b0eda8fd4f 177 | 51244c8e84a54353106454dfb1982d18c0d8d0bc 178 | 839bf0e8b8163d57150a28196f5e888be9d4950e 179 | e4733ed417a1a6de8b3f00913ecc0c277cf59bc0 180 | 5238c71458e464d9ff90299abca4a1d7b9cb76ab 181 | d98d1e9798b4247534c929dcbff21aa87e267c03 182 | c7c8f917739559ff6d07bbf43fec8f68e3a8dab8 183 | 6ddb4ba0f5edff34d34433a08d05afebdc2cbaa0 184 | 9f36d75ec0c09713c59bcbd44248d7761b47f654 185 | e4618580bc385b7d40d5d2bb806e78fdeb2bcd15 186 | 865d6ced050372f0015cef69de14fdbe6b4cfd69 187 | 408aa2a4c5df589979a83316fbafff4da68eb1a3 188 | 538d95bd1388456aaab95dad85564409a33e14e2 189 | 830324c1929fa8f947f5070c80e4a3c6c353801b 190 | fcf481e923f06484ceb10f195d222af578a255ec 191 | a80b7c5ccdcef089b94602b691da1c3d11de457c 192 | e263f0163ca92c4251ee2e02c4bfc8f70a422482 193 | bc3c998b71207705e9498c8ebdab2ed39223c485 194 | b0a757b3daae58156d660e5a9cf1f8923752040e 195 | 1c6cbd15ebdf9dcce0264104b66b689869322dd4 196 | 05b0d445de9f20aeb450ab7ca36f7f20d5dcc27a 197 | f854fca49d0ac4057bc6d4d754e4fcfc9ea5602c 198 | ff37e68a6da90e28993d5cb963cadeb81b7ee696 199 | 3bad14c3476df6c00d69a32fcb6a3af730d752d7 200 | 48f292cdfeaacb6509da30d6f6e08ac41b3c4f84 201 | 20c025b533a4f561559fd84c6b0c49621d0feab1 202 | 256c0ba60338b918708088a415b965e66b3bf717 203 | e8fdc3b4b312ee8725fab4937901752704ede7f4 204 | 5a016900d2910f9ee4ca35ba731879b551fc65ad 205 | 4a2d1c4da50c577c816d583255ef59a3d98b9db9 206 | 1331e7d81183b47e07ab6a0e3a1d99c1b8cfd06d 207 | f18b21b0ddae98109e486d391d7b0332acf6af79 208 | 6b9fad59cdaf5277904945fab5e9b740e45105e0 209 | e1e56be95d9e82df7bcf18d0c836e211f532a50b 210 | c4ddea746e35b4c9d8f8500b3f9598f08fb5f3d8 211 | 1a72e8c88aeb7ffef310a4e711327156b42a0aa8 212 | cdb83e8e651d9dd7b989349d1d7dcdc454ba65e0 213 | 373e36022e577e99e30ea074ae9a5425e9e93966 214 | 2ef251350cbcbc25dc640a561abf1976ad26ae42 215 | a3da3d154855b47533c92397f306da65ddd6cdd1 216 | a438c0260c122ee2db8c3a561c7e270941cc0015 217 | f3d4eca8a0c4b2023905e95c345568b11a46cb77 218 | 16d04d58ecfa2fc00cb0b57c6e37f69cf105d9d6 219 | 9461f3e49665f8fa85ce2c92294a80dc8b1ecb1f 220 | ab2c13ed4c8da65f64c4e3a82a13ce7d556c512e 221 | 2069af196ffab9dc07c124fdf8dc8dbbdc5f89ce 222 | 67c41850d115157687f745ee3eecbfed8620c0ba 223 | 1c2ee7a9ec3365aa34bd71e0ceaede3e47fbb45c 224 | dec68c90731653f9e1c150ec20d7487b8dd67876 225 | dc2bc43dbb86daaac7cb911298a9f0a227fe71b8 226 | c9d982233a2f69319ddf61c3ae3396ac96c66a16 227 | 57fbbb6aaef27b6f869f70eab4855335ca6e5128 228 | a06fdf2dac21ee3c17cb3735f702dd91f642ae82 229 | b39b5ebabd2f3b9f7bf877d4d81d6ec658664ba7 230 | b553eca948c1a4663ece9dbc9bdeb5b4cfa77bda 231 | f35ed08d96a4c27e4f6f77c01ce8dee141674809 232 | b7365cf5543f0d106a88a50479d1f13d3a855369 233 | d42325a4c1ea76ce198c7e34a3ec1916403a4594 234 | 9c98263709c2a2dbab6afbffed264c4864c8b4c2 235 | dc7e4f66c486a2dd149b53132d9bf8bf886c36a8 236 | 4df772b510b9e5803f1b0ee63b24fa84b068feb2 237 | d5a9944f3f0b2b3581c0610ebe614e9385ba2a12 238 | 4908787353570531f7f1078eaad30e68715768ce 239 | 42e1ae92487d4a3080ab4da738584af78f6431ab 240 | 2a6051885686ee8fd52044c4749ff0546049aed2 241 | 6641ffbaee588235ba3adb5b174379dec92a74a0 242 | 4b8f2504e43d11a3781b434696496e11df66291b 243 | 211fda0b1854f13a4d9e6bc1f9b3fb88d3ba2e9a 244 | 894432bf5a9ae9696e4fdb235270096a8466b181 245 | 0de9e5db9e9021e2233de55ff1ec965433d34d62 246 | d5b68d8bdee74b35c2b424f14c499c21d0bd7bc4 247 | 70332b90cd864109211a7a5019db2010b268aa5f 248 | 37b78a123007180fb40e427ec54f1b4910085d83 249 | 9372571037e0909f77eb2dfb00621af629fc7b77 250 | 2c653066debd8f0954a5460b7e7ea35505c2b2f5 251 | b1c317a01bb09c5eca00f18b1264c9fb413d687f 252 | ec40ab6ab03a960bec92292837fdb711802ae87d 253 | 90801fb806de06d62354e5ed586708f3e10a3d4f 254 | 998fabdb2b2af66b8d018b70413772883472d50b 255 | 750dff6ed2bb00ec0b45a8a0a5462935807906f3 256 | 32426018b68ca277623f6ae2fa3f90c8e8ac7385 257 | f7d3684631470967e039eac6318f560df925d6fb 258 | 7aade16426809b1f5370eb5cd01d71149b348af4 259 | 40befa9896eb7449b69ec437ac02a3c6ff827261 260 | 27f3468c1ca750e30112652d6e9fab72477ade49 261 | f3ce9f9fc938fa64fd3ee782844f58b2ac366ecf 262 | bf0efbde3a80b5dfba89b6741be79558fc0c5bf0 263 | fcb30f2740192786927f2665564efb44f5a6222e 264 | 539bd1330a35a3ce24843df0aea13342800e12f6 265 | 2a666f126662f2eace9d6d287ffe471f6788196c 266 | 3b760f81ffb4897b0840e5f0b09e902456d3b653 267 | 07009badc36ba4dba4be9af626a672c8bd439131 268 | 46c5f5e9b5204c9dee9f41d1010fc3635f3cb2be 269 | 43990b43486cbc373b691cf9de2801e66cd9cb97 270 | 8a8725aa7ae448810dd1bd790f19f4e436237758 271 | 274d5d11de8130788a7eedd03a321e6a4e08354b 272 | f607db42af2090807f960ad973dbce74d613e61f 273 | e6ea3a47f90c8a6e9cf0ae67c5c97b085e2e3752 274 | 77138f4e28a35bfbd25767bdecd0298e561e16d1 275 | 5cb03b1286c6b289981fb4ec338ba3b0c82442b0 276 | c86dcc1f043b6c3c42bbf5b8e03a43f60be1c648 277 | a509ea60e3ed9666bcac9568ee7062e3e245b27d 278 | 08e00165d6d4d6facf31bffc1cf3d228d41c3093 279 | a092a9d7f467d9b4794b9f50c77ec66c3c96227c 280 | 7b99598b1c94914df4837b9e4de6fc9e706de325 281 | c958c88f96309dae5d61d5f92c2fc5dbabd01ffc 282 | 6400ae87c54556bf36d0805e9bd60422602967e8 283 | ec19222886daeb58cac322f138b7f641e2d0073c 284 | f69941a24647dbb0e52f16127cb98c7f43bebe0b 285 | b311d5766f9623408747554bcdec1d8dc05eeaf0 286 | 48a08a3d09ce91baac0a0aca93cc8e9ba7310942 287 | 26fb22533492ab06e9c2f8aaeb22354125135aa7 288 | e54fea911823b975ed6f3205512fdcbf8d40b46f 289 | 88b4b67833eade9f3582a6dd7996cba3ba01940a 290 | 10bf3243ae8e19c9c93215e737872fd6ef5b3d90 291 | c49e40d433a36c8583fb3cbeef47e2b643006417 292 | c814fde86fca8302fb254f9d28bba18e1dec34bc 293 | 54912b5f2ff77a37ba808291a6f7b622da6b0736 294 | 8ffd8301739010ee56daf6c70c6ed534479201ae 295 | aae5b01fb0151ed790739a9d760706ece21afffb 296 | 544b6ab58db5093814d484422449fdd38b6ca9f5 297 | f5efd1c10802cbaec409b0e5d9a368d5b112efea 298 | 39542dbfdc49a52dd6d2125f3bf1b1c3c978f430 299 | db0f23feebe503ec3504df1ec8b0faf79569a173 300 | 80cbae7fd09a96687e6a2a7c502f783faa0ab50a 301 | 3f7dcc6127236f43221f1de2f1c2156d45ef8b02 302 | 5f2cb0a676b71961007fabaf1084a7124d72f476 303 | e3d776c3d2e1e60fdcb697e6336ddd2b6db41517 304 | c3cbe28abc8c0704a3bed4aad2de525fb4014418 305 | 010a233cca582483a99a92aac252104185db098e 306 | 77481ef0e575a23b44b414b50235d558b88b04fb 307 | 0aa1a3ab925a6e022c4f31d5ddeeca8e3a4aecef 308 | 1f0b64281f29841a78d262aa3dcc3cf7ab700ce9 309 | b724a6c8b2f608646bd47b4c3fb34ce42975addf 310 | 0b2e43da9e9864e42a199323fc1c0f419c39501a 311 | a11cf484453b5f6a7c030963c939d809743cec0b 312 | 507976272a55b262ecfb1826fbc5162e7484697d 313 | 076a3c0d7a8a870a158382d95548293d421336dd 314 | 538dd3dad376b4c519b6682f021524aa1ce43acc 315 | 6c91daca17938c93c5ed62cf8e809ef3285a9867 316 | 2996f769d10cb161ea7b2856e5ce445ae0af80ed 317 | d09d0cfa84ab289a489d80ce9403e802b42dbf40 318 | 8cd6a9d405a27df881642f5080ac114ccdc0980e 319 | 020816610aa73df4900a4dcf7696d95dbb8fee8d 320 | 355d16a97a1990cfd94f05d8c4532b9e5622bd52 321 | 0cf9bf3e7f038ff3df0e2e475219cbcaf6ec18c1 322 | d5714a5e149220db20619925b95a5b9db994dd31 323 | eced8bf5481cb3b316b085508e23cfad673d6412 324 | d46fe4736f91c549931754e3a0374b8784600f92 325 | 7b62f5fc7a4c14fcc51e5fbef8e133a11c14af56 326 | 0f7ff81f70c7c46197b03fa324e12fa73be006dd 327 | 5622c524db34b305c7e2b7d87cc50d2be5488ef4 328 | da23f71196545234b656aa8b621ff5fd5954c1d3 329 | 85e6efe3a9cb270248dcdd88ef4d2063820e3f2f 330 | 27c174814a244a65acebd30d74fa8d7237984729 331 | eb36f18290d4b657e93f1ee7177b9988d684682a 332 | 6e77bc3d6b3a95d7138ee24969d948886e51c617 333 | 1d50d58bf153552a06851886ac2fc0ff3b31bbf6 334 | 92d53ac46417132b515868dd9104a97dcadf5970 335 | 62be581bbbd41390b0358532bdf5800025fec52d 336 | 3b2b535075fa9c28882306dbbade3616f7cab682 337 | 407c158d9b4cf03ba5a7660752a103a6ec6a45f5 338 | 1c283e2c63782c95c7ca512c7db70a963a322e00 339 | ebd72087d7d89036f71262cbbe4b3c254d26e314 340 | 350a16f5600b194d81631d1762a4b7a4c1df02c1 341 | 725280303fae496786c00190f0558a15eb99ada4 342 | d14410154741026ca78bbf63dbd75145f38c4335 343 | d75a4ca3813e963bff3d7cd4d1a8a8bac4286e00 344 | f7f576e1f04af54b692b00f71840189290786c97 345 | 000b9c1208857ac2359670de75068f1efc7446ae 346 | b9b1d2980ae351aab62005bff4a22b675c11645c 347 | c3eda67f2731a3d0bcb5ce89ee795f419745a1f7 348 | bf2bf8a00cb37bcd1c4e77b91dcedc320b9baeb6 349 | a754fb26d210f18eed535826addd1e29bef759dd 350 | d9f0b7870f0853f728de678d6a80c0c0e142c347 351 | 55887e8dd8a2eed87de2d58af089e3069c298c7f 352 | 1f5852c79cb4b5824795e22f804c2aec97811eca 353 | 81d1a2f4733fd752a27b9cb1b296a0a8032e86be 354 | bf504ecbb5751827d9e9a0950b05a6165fc79e64 355 | e9d91b8f3f402170262431718fbb0ba9611e10a0 356 | e01fd29fb4f772826c299e89064fd43640998b5d 357 | 94db752d09f855fcdc195a1c44706c82835edcb3 358 | 3a12f6148e83a25ab95cdd1918915b700f42e29f 359 | de1f4880fe69ef588334a94ecf31558287fadcea 360 | 4e1c3dbb46fccdcb6e57ce278d14f28521f836b9 361 | 751bbc218d4d05d818f36c840149789f90124f77 362 | 94e65fd8542249b9b552288c6c73f78615468d6f 363 | 943eb3516d30cea4b98a20052548e1601868616d 364 | 51be0f9e0309e4f33ccbb14a50bb988500098ad9 365 | 46c75de29b97b959d8f3b634ee7cd0c606d75c9b 366 | d1375449592dc8f9fb5ebd592ed85a2a543b09e9 367 | eb9dc8a9d4e7c34342a2c0e3166c24b607800298 368 | 18ac199c72c4e89b9887a9fb2cc9837b971c8f40 369 | 9a2a57bedcaf2c1097d4615d2acc43eb907385bb 370 | da314451bb92e4cdeb591e63776c17bc799025e3 371 | 5e1730539a07c68d2360ac6af647e5287c282f4f 372 | 44eaed8d1a95b68f699e87a7b4e462cd2dfe477d 373 | a3be11dae74c0dc927dbb8627f2525bd2e2dff4a 374 | 0f76f9833cf91bd8437df09161a827d58f77913b 375 | 411664454859ee40cf80642c202c6402365a2d43 376 | a0fdd3d905e67708324a2fec77c11d175b466858 377 | 8cf9bc5a5f7fe50ed770dabcca22c4b703fe2d14 378 | e3dd7e774a1272aeddb18efdc4baf6e14990edaa 379 | 780b820b64669c8c2f36c1ee22fc780201d11c08 380 | da4e346828830cb92542767c87f5988857052b23 381 | 4cd4af50523092b223b10328fae1dbf91ff89dba 382 | 591e56d26ec36e1635315fd374b0cd3f68e07eb4 383 | 107a9c6c245a1f471568b86e0ab4bccdca2ef8e2 384 | 6a90b28e7cdabd24210a20ffbb627e2f484a91da 385 | 3f594dec64d4de2737cbe3d22e46548ee1001f49 386 | efc3a9a0068ce83acf9e9b3fb55752d01d348ec9 387 | 5e50ad3db1220d96948130269a5d98bd47776292 388 | a0292982791756b50d850dd0c1a80bc7680fd37b 389 | addeda098cf87fcbdbe13662f685156b9321e9f0 390 | 5b345ea27f70845da95e50bf84d3630efa3f84ca 391 | eea0769f79b5ccfc121dc5e49f7fc149182e04a2 392 | 962cd88eaf29271087669d1ca99690e4681db2c9 393 | c73cc138c86342b3ad40de6562e79f4ebcc56a4b 394 | 7a829aa3e863d0228b1173eb2ae0eb2ce6987ab1 395 | 35adefe5cda61015887a6520c502a521afb2a3eb 396 | 8b3b643b81c7ddb81c6e8ff2272a62f7c8117bec 397 | af872ecafb54014d1bd1f6516432a2ee35705f74 398 | 858fa2f34bd42922d28e1149bcf5f393a8dec0d6 399 | afe7e76eedf989f599b4da3b9c3b73e5e0653327 400 | cb9a50d773c05076a9e0f292a8c391ccde26b24a 401 | 3c5a557b9498c21d3cca550072f0b3cc6b867b18 402 | dbacbd09fb331ad2aaf274c5ab307614af913cb0 403 | 33271bcc527e87dc5611608006c7137d56755f7f 404 | ead62d7d0207c51a53c00395ec28399210f11a2a 405 | 7d44d5edef9b6f21ae780d1918dc2e28ea4e1ed2 406 | 0eacedeecd5f13a5230fa11e0579607cb9be61de 407 | fafd2f0c577489e4459d5c536177d0a457718ddd 408 | 096d52d217ea8d538b1de9ae55ddd238a0f37ef6 409 | 732dabd6136762b97c9a15fa065a9c7ed6124d33 410 | 49f308101edee5b9e582b903d8d0bed5f0654c46 411 | 8592c0f7448b616bacdd96945125ae1c5bfe0f83 412 | dda771eef51abd620a9f1ef0d99e7e386453783e 413 | 217dc5d098d0996886bc45e882ff9f790c389818 414 | 808798fed106b10e96da84208e984e3b97c71ff6 415 | 31a5c0f62eded254e70bc658f27d88025a9c9570 416 | eb622c322babf1b9f279d4b747704f9f77bb2605 417 | 0588150e1e1068f427692d25a8193333a25eee58 418 | de53f34fbb2f8824985630a18ceca764fa5906e9 419 | 00ebafc0bb36201c00dd34fd10f952bfce576983 420 | 44f097ce0935beb66c625de6bafa246c3c09d9d5 421 | 08b34039e0fb98cddfb08aaadaaeb1d40795207d 422 | 18e537ae0128e64412a4e937ac58ffaf5a61ae95 423 | d1117d0edde7e003afc069d100fcd43c53aec8de 424 | 26cbe5d521862281eca25339a4a4b39c04b2b5a6 425 | a85714198a5fc8e52744f9fcfaeede6149aaa073 426 | 3ccdee944daa116629096eb76e06824f09209118 427 | e08ebcbe2b35da55f465566e682a78a4fc48296e 428 | 443dbdabe18f1298b135d8c633ae42980d4573f6 429 | c71e3a0989754d4ffae45a1c6ef8e348539cd83c 430 | 74c70cdf75a9e6c058b095129ed2a14ae578f5fe 431 | 7b4b37e35d1bb4179904134e72e767afc379fff1 432 | 9b9bc1383b98bb3933271cba2bee375b41b5be21 433 | 081a3397554c28c556ed55ef2e53cd0d5572ecda 434 | 3cc182fa65c7291732321bcff15e2526e532d0e0 435 | 259565bac466bfd5130b2e725e639dcd36bdf24a 436 | 4b590ca2ea98d90776691700f130225dd3813052 437 | 011c3d9bef8c9cbada8e687070c42124bf59f138 438 | 1e85a50023ed076df95d9db2f90bd50ef44866e3 439 | 7d6a305fb22e0e6013d93f4bb5e08589dd0b6a0d 440 | e2e2e4fd164440c907c5213a389abe0aeaf53911 441 | 9aec906c4bae714c20e0d070c3d1908160f5765e 442 | e1327cf181ab30ec90f7da63b6d91dacd11de1c8 443 | e3b9aabab8abf28430d8f4bb93711815b23c2157 444 | 0d0b1d2c734eebd1a96d0cb1e0e296650f6e2309 445 | 1d923c954d8901d559f1262fec66ed08fdac73cb 446 | 126b91409d4625e9dd49b726db33c4d0026f0818 447 | d9ef9f3ca7a7c49ccc5ecbbfb700453be9dafd13 448 | 13867fdf5f792ac999f6131c34fee18dc4cd175a 449 | 7d5b84b587e67d6175ea06d749d0fd80279a625a 450 | dac2ca248c6c9147f0655761384b293850b28dc8 451 | 87418fc8939bbfff763ce1f681b327a8d4c4933e 452 | 520b411f45f9eafd44c612023850b87375bb893d 453 | 1e9d56b215518e06243e4a076e2c883fff3a7a54 454 | 6b0ad30e480d09da203f209b811727c75bf9fff0 455 | 0322d7a3fea8555a01398cdd77c8e6e7fedb26fe 456 | ad41b37b5dc25c5421c356706c5a296e50977b8c 457 | 109d6995e8900e226e0ad61312e60309849accef 458 | e3ee9b096cfae1970c884b1fcc44abb1513aa35a 459 | 9c8b7546201e5a9019b8feecdc32e2abfd53a8c1 460 | 9a0fdae0a9adfb030e3fdb92b5d887ac2df45d82 461 | e953f6486c668ea5a75ebd211e08017854381c9d 462 | 484dcd4c689238d0dad4b6cb1f8a9f58795b0c25 463 | 56f794a17665b9f4dc80f0ab2a4853f7fe2dcc65 464 | a720c682b2149dc33448eb005414ebf2cf480ec5 465 | c6933f0c419a2c0d5dde0d31250125d586459327 466 | 4c51d10b263f8c06ff90dd41d503e19687a8c919 467 | ac8dc3fcfa4e9e91dddfc0c3fe6d7e0021292036 468 | 533dbbc7863994846227610e95f9bee72c954084 469 | c77882206950c352688c3313d268c05784f4b633 470 | dc404df7d0288851d5f1a136f4346342cbf252e4 471 | 1622dc9d9e5423d7b84122f9ef7edfa1981d9960 472 | 5132cadf63a0780963010c124355f88c8df9a0ae 473 | dca85b9508dc6c8879e18dd427aa08ca0176d2b8 474 | 7d376cde64e0b170679b05e93627ceff35fee53a 475 | 74a966bf6d512cfd591de488e7470b0ac8af5da3 476 | ae2d6f1904c903bd8b9757d722e0dda9b31a3724 477 | 4d25376f5fbef3eb0eccc7c6076401dc5c29a415 478 | d5cb23329fb9bf8209683326dc505173f516355e 479 | 522f834ba41fdbd3862a3b059177237f46e64aa8 480 | 9e960a119d23bf66b6e1d693f713606d7b7901f2 481 | 076e100316ee524b937952555c583dcaba269ca3 482 | 99d9196c6756d13f60415e282bd2c0b108ba3fa6 483 | 69b1ecb2fa9f970949b5e98f390de42711f19cb6 484 | 8b59d9a27ac5a34cd1de180346029d1e102d29ef 485 | d1127c4d5d5b5b8e19b3c0984d457d77eb703c0a 486 | ac1d915ccb6dd45c177e67f34d9dbd7f3c4630e7 487 | 369c94dfb792bee140b3118462f165e229d6d244 488 | 99218a46e018d3d9ff3704cdfc609fc8d70c139b 489 | 6374f83fc36a49f7b94a5f812a5b15698c16d58c 490 | 32b9362c35e84c37cde268cac163d6a52bec5cb6 491 | bb0c3ff98174318fc75ad684b1efbc923556f03f 492 | dce86106b6cb9235d912da8515764f25e37fca25 493 | 93d343a0db42458ac8623f5e3f9231a81e8b693a 494 | d185ce05beec9face17d946a5741d144747a89eb 495 | 5268fe211fe08d06adbc32d9b3ee2fd43b81800c 496 | a2ad8e8f315f29f46467aa4c67468343e34208e2 497 | c2b0fefbaa1a7be9dc53e4294205b930e06dce6d 498 | 374899cd8c36ed948fa840a4a50cc219e1bf8edd 499 | 67e8701517bff744e4db629c062a01f2fb738eb0 500 | 3f5c09a50b30121edeca9071b4b0dbf3f2a0a794 501 | 7cc0d713c9b6aec5265569a687a99f7d10d430d3 502 | c2d511239a68c5bc137d5e90c59c8b17d7b9ff51 503 | cf1354cf7e9c09d6aad48ac76b945191bfaf2733 504 | 41874a13e89cada1624548c668b82fae12e3f0d2 505 | 696c00b48917d9ceff8cd1f04ea11d0dc16ad384 506 | b80e4616e909ac0a42c2fce51ac0515669dfa325 507 | 4e1e07de1a8ce4382dcc6454ac0eeedb93d0ad4c 508 | 3b566ee5ba43ebe0bd9d97e56894987dd471550c 509 | 0fb0017f1cf4e55c8e96dbc12d8c378a9d474a35 510 | 33ee015e139f1067e445905bb3946f2b9e285b86 511 | 478f4aa7c2addc0c121ef706fcd7f849d238e83a 512 | 7c34ba8e83f62fefec341a953adade04b8b2ec8b 513 | bbb5d517deaaea6c4ad078cbb60c0b35dcb4c788 514 | e0dfef10a1c8bf05731629c9eb8b0a7367bccb12 515 | 9f94767922613ebbf51ee0914403c86f7ae667cf 516 | 82ea0fb51c0174c492a52baafdfddbad35b630f0 517 | 168e3bf34848f9a3835b9605525012ea0ee6707f 518 | fb21553ef7d883c5963a3dd6950edaea57921e23 519 | aeab34df998c85e71a89d9b77b83100142c3506b 520 | f1bef55acc52944fc61db347fc0a3cbeac7465a3 521 | 7028180ab007a49f3559663e3b80c1911001411b 522 | 7305c9eb35b3f7dfd9f7da77c5135ad23c05ef93 523 | 86465afc4bea7fd45103e1a281958ea38b89ca5a 524 | 98753000ba66f60958378cb52b86372d86d052d9 525 | ca2245a87547a6b8828c9813bc1ad8277c3406d4 526 | 7288db58f4c972af7616842081ffe15acc21ec86 527 | b36c3c5a0d0bd358772365553ab1bc692bf2e2ee 528 | 0ad3b28bcc5fba4aa19e054bd867c9b01372197b 529 | 2aa7c876ca378e656185af4bb6acdedbd5fcb50a 530 | 8b39dd90409bd512a21b7ce30b98b4f7b8dbd462 531 | ba12bb643609d203061347ce3cc60be98df7aa67 532 | ded452852e2e948cd5c15783400702e410922cfd 533 | 448ca9562c5fafa484aae868042d3c3cf6236321 534 | 2537edbcb4b789f89c731cf2b23d20cc32233159 535 | 5d7784fc80e4311df977ce91fe8316aca799130a 536 | 2aa4a44d46960f9ce9f1e01f9660091ae8469a8c 537 | cdc68fdba960053f03a77d3634d1fe2c27cdbb01 538 | f71c2a3038d61e619f613cb91b58988f43448916 539 | e5f265bff11a6318b80e89edf0aa02be48cf8c77 540 | 3cadd32174576eb5e93b27d14c0d7b0db9545dce 541 | 3914a8c3eb804ccf0a9e2bb72b9f341b3b31454e 542 | bdeb96f9dad203c5132becfae7b5bcc3ff0dae7d 543 | d615cf4eb49bb8c27c91c7783e5434bc1b0417a4 544 | de9569c0998702f15e7975302073cabdc5138973 545 | 165f744fbad11433147e9ae42b128b585d1d8ef7 546 | 239e9d000ac8220ea522c3d40c2c1a4525ec9cf7 547 | 17aab519b90801d69d0f903f0c4420a26fc2e8eb 548 | d9db7fc5be8d78cff18a1080f8ba0d103f811059 549 | 20142f412db4cbea6e2d553d2a54c1d3a06633ee 550 | 11cce1639b6390132ea91008698a292097cd8433 551 | cb7d31132e55dc88a2799432a69b7c5b65807e1e 552 | 6b5f795e546b7eb40b0e791a094cefcdc6bc759c 553 | a7ee14062962a1f2885e3aadc8256b5d9a974cfe 554 | dd624e26b46b07c63b6cf29a2884afcbc121cceb 555 | 32f6ace81715e0872e6db7ff4a280185205620a3 556 | da74fc67254392a7eeb9eb65461d53fa91beb5f6 557 | f7358ae9eacc982428ea03cebf791d5d424fcc3f 558 | 2dcb66e56b17d6a45e20f9404c2531b50d57c690 559 | 97b3d797f8375fcc8573eee1f699da45bb7303ec 560 | 45386578a5351b95b75c7c7f04fa9feb100dc8d4 561 | d3e8e92c05413e71090a2ee41b4d022440eaa9c6 562 | b61819ea931a65fb0f112f650474ab2de7f2abb6 563 | dc9372811e84ba153ce5d653aa4c4ffe29591d9c 564 | c6c16f6c37ef55eace481beadf87ad98b1961bfe 565 | 9bffac26f557ea52f130a0a88aea5c92b15b7a1c 566 | 97992451f4c8324674d17fe7ef36c4de32653456 567 | 6b75ff72a8d384d63739eb0d5d71d5f4b0e9b396 568 | 9d456fb3b28c0164bb2dc9965c781b5524fd164a 569 | b5c98d264d25584b276ae8301aef37f3ed4d391b 570 | 8ef2573f38c4428edaed17f8cf84aa66f278448e 571 | f83dab1341494f66103cd457f64dd8117f8ac715 572 | b034b761c3910ce331cb906ac34ca9952efbd815 573 | 4474bc385d9a622644b646f1945c0d856df979d1 574 | 2ea86571c2101627986c14c4a7a051a1bb3fc2a7 575 | e5e518cf7cc984f2afe70e40efc02084d766e394 576 | 3d1303b05af8b7f89ce4c33e5a70ac9f572ea5e9 577 | 4c0f117539562f73307efbcd173d006367ab29f4 578 | 9590d8f489b1b90cf9c72984d926ab4ba7e62738 579 | 213413ce727a30f40d6355f60d5fd5d83e9828dd 580 | e024b6222ef55d0f7201c0c2ac8987d5bab60976 581 | a163be4e3eef4a799a82f0e8afe0418dd667e0ea 582 | 3c4df66b15cebebe14ee4ce2a472534f39388c59 583 | b41a210cda506d26a22c460f801cc04b10cf6d4d 584 | b43f42b0fd15d08a19c0f70ec6842e854e289dad 585 | 3d51c5d8640d7ea5536bec237a670940600aa3cd 586 | 8ad2b64f852b19ec74e64ab116a71f17fc569bbd 587 | 650d0497e014e60d4680fce6997d405de264f042 588 | 3cb870c9bde15afe38879929e25b57ebb538974d 589 | 08c08dd9e2441e122f9e96c2a3bf731be6509326 590 | fa5ccb10d39fb28e73206cd7df592eb95f362498 591 | 8a892e95f4ecd58184a2b768e08c8b5003239bd2 592 | a2a60540ddaaafcd009b728f921a966b898804ac 593 | 4ec1b6b9696566dbf8e20e49ad3899f98d6855f3 594 | de89566b3cda1c4b7ef85db035d747eaad0d4081 595 | cff01716e3b722613b3d017b9ed5da84d24e4fcf 596 | fe53e84a980a94f04f32ca0c8684eaf4d33a9f57 597 | 5f6d61a4dd88dae5789128f69597f2f8a1dadf02 598 | 087e4b851466e05684faeae05e43f550fbfca90f 599 | b19c752d99c1943bdd6e537b0c6e0324e86afb06 600 | 6685bb1f36b324c8c52c75a003566a3929749b3d 601 | a14e0f03db78acd1a9b5f1798d2f521899facf8a 602 | 3b3d1dd521a90456cbcf235c3aa69dfede99a2b5 603 | c85530cbbe6b6d31b3bfe0c56e1a778aaeb1aa76 604 | 2797325fa11e6ebf294902560f7ed5c9c664d246 605 | e32d80e59e7b8767c27496356398462a3e83de03 606 | 41e7bc088d672a2754945339cc06dc3ec7b3bff0 607 | d1541de60f423d78d3a4adaeff2b887d6b49260b 608 | f80e193b41381e20f3dbcab96396afb1197b1e9a 609 | 761d91e46ad2c3f55d9f8da1831550b48c71e006 610 | 49d386a217f777e4b6c3ce956b14b8a7e60606c6 611 | ebf99c988693a344ea10d12077d665cb2e82f416 612 | d012338b7a406db3ec210dc3eab978f74ccd0789 613 | 5704ec8757210483aac46470d29da86789edcc74 614 | 5584e6453b1159f28b19b7482905e5379e9e4d62 615 | a66b72137e81a2956d807f7997158d3e51d6ed12 616 | 5fa8876c7785ee929a2ad02d2dde317feb6726a2 617 | d1848d4c7001833186a4d22c19e8dd362de5de7e 618 | 8a09a98af26833cf0110c0f38da6bccbc5846250 619 | 5c0b8914ded3474957294e5766c9ef03994f2c42 620 | 644926cfcd48a1018f5581530eca1ec9cc72000f 621 | e7646bc29e018235523acd3ea2506c333b022986 622 | 5f429cbe2eb9c74b59014c2a7e946b3ff0bca386 623 | 4820c5b0dfdd8750b9862ddb205072410b58359e 624 | 9ef44c81e7cb8307e13507ddbbf7cfe551f00f78 625 | 1505d832030dbbc8cfcd0cb71aae9cbdac624546 626 | 44633842b45f650abc88c591e37dff3b805d451b 627 | 5a9d4f3a59963e959f6d884de3aaac383afcaa28 628 | 284d49715939e01950172a35cfceea4a29598495 629 | 551222dc9f6f3ce2ec65b338549e35a8f8f14afa 630 | 09662cf15b82cab09585ef88bc2385c1362848b1 631 | 51ec25032b72828897e9f1b387b6b77613831fe3 632 | 42a8c2fae7d9910c7c8c6419464d56c09d8dd3ff 633 | a24df79f9cce7e873576e2f18cc1329ab67c7346 634 | 9a645cbfe90be156b2e5854975d0be914e5adf27 635 | ade31530cb83ba1a1395904c43bf9edc423682eb 636 | 9eaf83ecd9be0d1155eced15a730262b216b51e4 637 | 75e0c42a60e9bc0202f0dc62bb1aba5640a29418 638 | 5db35d4b2ca80087769035609fb548bbd67f0e1b 639 | 6782c4d282aed3d217733f49e6365a26d4c0467b 640 | 9456b17cfeb8482699eb00d6bcb28baa38baa2bf 641 | efee0ad8782afa077b155dd689ca009d89cf0ae7 642 | 574138ee21f440572f1eec7039c56cb1fd903270 643 | 8c43a604bff76c7ad9ac5707cc07d66174bc9d13 644 | 54cb895b7733ade4f8bb8160b3df5bf26e409f04 645 | 45ea6f0a64672014932081b5c367b8ce7b8b4162 646 | 7e5ce8ec782aa9a4209ef4c51b76a24e1eafc77b 647 | 07b01138c74a31b72ac4935dfd4a6ebdc5d579bd 648 | ece0d2158e47b78291b25a9cdcc09a9104bcba46 649 | 7def20e187f9dfb560cad4612fc8b7dd904f0a9b 650 | 156f55fa143a76a54c105a986ab7a40c0270596a 651 | 7e1d77af03005cdd774edb3b6a774cbf1498c58c 652 | f0f9b72fb282f96f72b6c2f59be42be6d74ee619 653 | fdb170caeb58ec28ffac26e4bd4b05fa7fdd9980 654 | 6c52a247fb7beb75fc4bf1cec36682a2cee93fbf 655 | 6bff8188db3cd6dbc08d4d1bab225fd16835adbd 656 | 0f3feb07ce083297f93316ef8cd349687e65886a 657 | 508b099f172e208bc399249f7549ccc46d0a9d21 658 | 5ac766f37ed314c7b69598c3abc2a14acf6a7946 659 | 17cc042d8cc4b0f5e6a86ec402e670217db9963e 660 | 23681fcd712bd5c811c5de0367fefcbf2465d8fb 661 | f93515ff942cc4972cdd2ebebafaf96ba923942b 662 | 0b5d9a7ad8236b61324887025098d91db6200de9 663 | 354f959ef507d9fc91d77677f2f49def5a8f847d 664 | 7fbd310a1e16b20e79ae36234dfc9a57aa8818fa 665 | 4e97a426c26355f06b3146c612af1aea403e6131 666 | 19196294716b8dc6360da76c3b31b51de15f7914 667 | 669b64410b7a21c1b0ebab65b9ade74fa0f54a43 668 | 0da2efab1fc5dfa8d395da6602c18056adf20c5a 669 | 5d8be408bac89eaf190022c784f7630e48d6cd45 670 | d91d6927f3c3125eed146e0f86760ef289a0482d 671 | 55e357d86006cd14a1d5392862d456b34d56a6d3 672 | ffcaa48b293704c503a12c426fab2334db8bf0a7 673 | 8db0e896ca30b541a764fffc77fa1f3475633b5a 674 | 29ae378de87dd8b42eb8293c8f7269d99a55cc93 675 | de0faca8e1299d2facc511bc90f3a2fa51112460 676 | 8159e50bca7665d05d491c6cc91c20155ca7e486 677 | f53a77c36900e38f9fea1e4f4c255ca54c47e24f 678 | 1825c696ddecd1d511868152a7d2e55efdd36b45 679 | 405736b036c5dad3dbcf3bc73262382eb8991b07 680 | 7d32643204dc84f126f373cddccd4287f2b1d818 681 | 1575d3e940d067f82e885ad85b12666aca7901a0 682 | 11edc0e2a8718a3aa18743f113977eb22bd0f66e 683 | 8bc974a1aefcd3294c6c883523596dedc8155866 684 | a9260349641bc1d1e1ad9288d7385fd4ba1824b9 685 | 706af9708a8f24071281c7b7be7fae6a83792f83 686 | cdad480841fac75927dee672b13305d90148bdf5 687 | 7b70d13c4c1f67873ff5e8b66f166646b1de85b1 688 | 5eb4130f15e1086fda7b47ccbb7258c10b14edf3 689 | 1bbcc90e23379b3515508c5f7e2529c32c45c0a9 690 | 8addb56a862accb551b089dea3ab2b5e56a9f658 691 | b3516d45fa54852ee2f5fc3aa93326ea362bac3a 692 | bc41b37999975edb22b03f2e46cc71ad0a383b2b 693 | 2e16bae0c048168457ceeda1bde0e99d0b5867db 694 | e7450897e4b445dc26f06a62f913721bc8d4f3a7 695 | 6ed18dd64f59303f85576c0277662f1831d01abc 696 | bc61d628f333dc1b2a4a36141cbc3a26101edcc7 697 | 41b7291966967cf79a8ef0569bda8ac48ae3d38f 698 | 1164f6a5d87f9fea9ade1e8aa89bc733bb40078b 699 | 906fa77b38f46bfa0b1d3d7481f03484b27ace5f 700 | b79bdfe99f04bd41bf0550b942d4205dd42a28e2 701 | 1fa84b9267304a5dd5431707b628f7a7caa555b8 702 | a815e98554a4bad30ed5a2ab1163bbf0229295e9 703 | e3fa630c413217bc53240b0044c905e772abd888 704 | 7a86a4b37885d1271a091ce387c9d6fcb3a452a9 705 | 629691aa0bba7fe289f366203aa919504b595a86 706 | f8a4506a4c3bf03e82f253885b5f91595942a03c 707 | 565e60d66ea26772497578dc33ccb15026a47212 708 | 172c81c31c37a7b2d324f7001877d1a888a18f5b 709 | ef6ac11285377cc48afc9c78ba3a1616aed48ccf 710 | 3f62b52c574a2dd908ceba6e7dca3b1d75fb81d3 711 | b087ac6b7b40b2b762e13990fee4be1f7fac7b0c 712 | b9c840e476a4a38daa63c8bd0043984a3de4b09a 713 | 624d1df6221eaddeed2948635e2cc07b335563b4 714 | ed53d1340b14f77e7f2c966dccb6a082d17265e9 715 | e28f16e01f37555e5d1f2843982e25ce1e911e31 716 | 8887b6d38a038e5c11b09e6fa6a28ffd3194daf0 717 | 35bb37e9c21af4cfbfcae37b7e376f6ecd027c20 718 | 53fc962a95207c392625bebc9d23c19a2a8597b8 719 | 8219bdcc8c109b0d403985c86cee89ce7aefd952 720 | 79dcea535dbe02efbc6c5a69ce6fc68d31f3ca4d 721 | 34e61a5a0199c20c930a00f1e47fcbc641d2e992 722 | d3b7dde878921d5c071610c7ea68e36b860a5bad 723 | 16bd97cc7bb965b785750ba037b42be0c641b6ab 724 | ef8ff2b0431fbb90a361c5ba53e743031f086390 725 | c4b1bdbcd24ff7015daa56520b08ecea8b982847 726 | 1244854feac6bd417a963abbb524285b3efc27e6 727 | e4506270c71720282397238f34695f855a74fee8 728 | 6fd4b357535eee437deb6747d6337f4b9d889d0a 729 | 3ab0297f916bf9a09c71fd46c660c007324ba9c5 730 | 7d38a4b775960f714168391b264a36e206730757 731 | a87b5b20be3a542e68f6f77a7ca8802e546896a8 732 | b109c715118c696acebeab90621f69ae98ac9c6b 733 | 92b0c556700f88cb338aaa77e5bf22f5d3d8716d 734 | eb9073a09396d7e3e11f93208d9fda49d5cfc263 735 | 710858a70ec6dee14ee7b28b9289377146ccb86d 736 | 71773aee08d1f95233a4e17fa1823c36fae107e9 737 | 3a56cba6aa5bf1e9eae491b7cd355af4887ca895 738 | 69d0d0fb385743895affaeeb844e5d981619fbbc 739 | 879edbf57833e78c83e14608f1f02d44e37d1bea 740 | 3980517ec0eb0f7110ab7617e532eeedd9805ef9 741 | a6170b4d0b3fa95d47c9cc4dd06e40fedc113c76 742 | 1e7bfceea0de8f2c7302e253f91c6285c54be258 743 | c43114f0992ed59f3f0100409c0b101e8e277bf2 744 | 2289dbdcce517da36ce2d74b536b23dda5c2f0c0 745 | a90b650142c41840fe19c15569cad0cfded5dbf7 746 | 35a41a36c83f7dc347e5d62ea884f76b38f18c31 747 | 79ab275e703a0199669a1d0981f98a9822df78f3 748 | f383c3d9e629b9775b32428f02c871327b026079 749 | a3d113819a58a2d635451873d551a4e332b9b35a 750 | ac9e695f989a31476220ed4c0668f1306ece96a3 751 | 7441b9219c59544f3a207048c3033005d1879dd4 752 | af329739254971ba5f82a7dfc9728df199675ef8 753 | c2f61de0e08a38b625892274162251b1bcd23f3c 754 | d0779aa6fb45a56bc5b4a61128da8adbd3096e40 755 | 75a125b98acd64d4e4129419750cdc5f8fea974f 756 | 689601923f262c1e23b798ffc5c8018d27f42a16 757 | a30f31d1076b1b02fa1f1fb2de13d22bbf782c22 758 | b4e781a9926bd4940f538aa6a73ff65cb1e121bc 759 | 7cba885580cd40d221ae652d71548e8e4ca172cc 760 | 41a7c52bfd3ba7b7c7f9e0c4c2ce45681f0e2379 761 | 19ac91c8119a060fcf1c90bf60d60f71424612a7 762 | e48ec9b2bd76c506631eb6b57aa53fc2bc6ffadd 763 | 2d5408d96bf0d01b6c20d95e30d81f35ccc91ce6 764 | 87a8b5033b52a52521ef0a8113d99613cbdaf88c 765 | ed005c3509291ff1daf5258a73ac0880820b997d 766 | cbd563170ab2b79eb7e63a702f024a373b0cf81a 767 | c7ce60dc88d7c0c0f2785de2da2bbb9fdb0cb886 768 | ffa14ba5ee8e6c7be572134b0cb3c924dc53e46f 769 | 1c822051ed9357077a844e7b2631df9630f931c5 770 | 0b1aaf6c7ac70defd97340954259c23db5db15a6 771 | da408d65502c18bce39b12b99aeadcbfcc0ac908 772 | a4c13597f98dfc556972f4355c74ab8bf981cf4f 773 | d72b52974aa2ae8293064750f0f0645626680ab3 774 | 20cc14505f308e042878bc69f936696a117e6910 775 | 74302106ca9aaec97a450791aec4f23bcf9ad13d 776 | bea6139b54f78420d4d470e5de21e3be9eb27b02 777 | fef8ff3234e373d36782c5ad91bb62f5c2065e12 778 | 5d7c8cc11e2f5f00af866f3d47e069e648c44ac3 779 | bd955d93b85f51e3190dfdfc19efb83f6b039f1e 780 | e48cf91cadb80890270412121624bab1edd829c9 781 | 0aeec7722602f15f6208945adbd4bede5b06633e 782 | caed4660416de14c89634cf4a0f95752d7654436 783 | d5563882a3a7626037bd60fc8b8bf56a7bb09ae7 784 | 013e1bd30e8521056e5724387b88af6476b56814 785 | 11c51072e80db8e30226470f64b378bb762d8801 786 | c889021b0223db5751fd2da74c2d99ff61a5fe8b 787 | 80f47af948156e8f1062f2cb84d0b83833520a03 788 | ca0f3d032088555378d10a946aade2f5963b4a5f 789 | 76cc3dad9f2d3030c7f8ce853a64909ae9932c75 790 | f0727842dd2369923355abae242f63b6675bbb95 791 | dc081c65a2565e94ba8447cad43d51c9fecee47e 792 | 6118875857606eabb89b9cd65d94b0fc4bbfa688 793 | cef3f2a90b939fc0e89bde300d8c470f913ff822 794 | e2a9a350811f5006f78504e1dddaee98f69c30d7 795 | 2b013037f6bae88ffea76ab9a0fb07169c517e15 796 | c305ec2ce8dcf5e04857e6fa88fb17de3a2d21f7 797 | 1572656254d2be5e893d40c564e932ab5777899e 798 | 7acfcfcdf438a3d171d0c37ffbe62c3289f197ba 799 | f29d906b4fdb436fbe9eed25b994e77764bbbd6e 800 | d18bf9a036e53c79840defa66bd8de074d141703 801 | 87020829c9f4c60fd5802c6b6d32dbf95cc00176 802 | d6b74fc1e1f558a195a7d5c60a6fd4cc27911a2e 803 | 4849a3fb7e2039b119f9709282563e9913d2029b 804 | ad6fc7e7eb4ba19ea458cba1f61f93a6a0844391 805 | e2e34e7f3f1fad3972edefe8d32f3c8b6d5ac0a8 806 | c4e745971779a30f754b68b912968c9c0dd4c0dd 807 | 63f9d81aab87ab9d7bc02b2e59d641e31181d2d1 808 | b2fad779804b582b77a4bf54b5717f532a47d3fc 809 | 7368c056a5fb843591e9fc1e8d965a89052b1aa7 810 | 03beb15f83f5375a585cf86e7432226655bb4930 811 | dd08c06c7026a7bc8258a271b03928026e1e4efe 812 | 9d7e975b7ad3fbd2618795a2f5912469e9714c6d 813 | e0aea3d5c4c401e54c73493e74b6e405c4ba2bbe 814 | 01e92bd2a8fa68da0730f0b35057d96e0a64341a 815 | 3519181f3a438a21aceda12bb89ea9fec6f8fd8a 816 | f0efb78172b6099a29764cff033222485ca5c2a5 817 | 7ef82ac78c9c0310faeaa5cf1b8977bcaccebd91 818 | d6a44dbad15b0827d1395e284a36d39de39bdb61 819 | 332f60cde0ea49c902d754267e6979db5bdb58c3 820 | 24845f8ccb521390a9320088309b703f638e4254 821 | 552858f6472ae15d33ef7d1d362396ff1aed8977 822 | 2ec58c020e289492c1c88c2803cd9f484c9886df 823 | eb39cd370f21909c060232293f88a320d457bdfa 824 | 5f1d60b60d1c954a775bcef726a850bb4c937a9d 825 | 170eff47c3caeceaa3540c4bcd170d6b32d756dc 826 | 0e6745c272829d048682a9abbba3b1288b14e031 827 | b7637efd5eab03a83ec7946dd58079215f8e0f0a 828 | a9bba2dcc458c01c1cf26a87936deb38030669fc 829 | c8a31efa8b62614371ea2bede32982f6c736e583 830 | cb9d73237b18c62647ca170c3551f2d8667b497b 831 | 26a4f8a35da851277c2667a2635e4e1739cd8791 832 | 04d74b43e168d6ee2f5b6bf4b41cec2713b0c852 833 | 0163fe9f9a4e7578e9c2785830d92539c4ff4ee3 834 | a1a407db8b7b39cfc5fc8c2ab8a0030591a2ee95 835 | 11d0b3d44544dc1f18d484f4074f4b8055556fbd 836 | ce8c5051f4ee69d8b28eef581699e44efadabbeb 837 | bff9f24bcfd7ab47c9aa2b2599f317dc07a43d71 838 | 195a3c17586b0bbcbf8c06973c92785b6e24e58d 839 | afe66e0314eb15a5cd01d95b94166ce995c3347d 840 | 87dbf2f60db12a53c2c0f842de0f2e15d2be7b61 841 | b91a521ef9d0dc1717b7513d0a78aa1ba3f5e6f4 842 | 6bad08cd14c285d3c92f670c9b2f57e2c1361ead 843 | d0a24be283dbd352b6a375b519a2dfe24a1ecc98 844 | d0a6b67f0166bf1283c88528abb731ad814d9d59 845 | c8f1a4cfc1735d6d0c418baf543225ba7ad5367c 846 | e20695becdb3ace57958f403422099458945b76a 847 | 11f64db1966324bcc36ab1bc14d4b8314889cf5a 848 | 4f5b492a057d1848fc94edcc13b7fd373e805f1f 849 | b590b370773a85ef309dd87f1e73010073977353 850 | 1e1346fb66156132aab0baa7c193d187cc13b06f 851 | 5544e3bc56b613408a67084d542b1d27b9644b39 852 | 8f0e683058da332f749e8a8a14927b4ed6228198 853 | 4091f6e669a52e67cd69d66aa360f754bad8b81f 854 | 8e08d666f4a43a735569e5697b0ac335cc320779 855 | b98dd7010e35d6d7bb2ec505d2547a3932c60f41 856 | 2e263fc5ab5d837ae92e051727c45e5157bb816e 857 | 4e3574a9bddced9f944ca3268d52dd430544e793 858 | fbba9341157a78ad15738bfca6df21c7d9cd6edd 859 | 38bb44f291d6dd90dc933fd35d66fa22dbdd028a 860 | a92d7833e14260e979a42bfa2bb2a673dfecf7a1 861 | c3a37323e51f7a993c7d9b3ebd10b63ea38a87f2 862 | 4e047708f91d5e689e8b4b4045fc1724e0a5ad90 863 | 5425ebac0a737dad15fddd0f59a8c0aef373af2a 864 | 675044f626276f9656282f02bde353ad18dc9fe8 865 | 37afeb0c3172bff41be664639294db4fb956e71d 866 | aa914dafca972e0c25fcdefaa3784c6b8539d597 867 | f8d55eac08ebc8db50964a663febbf5291963198 868 | 288df9438da3345226c5a36312c1e97ac95d55ea 869 | 6c89314888c2cb3c9111662ed504b064e0a9e1e9 870 | 3462e57e39772cc2a57a79a855bd1ae624d658c1 871 | 62d17810684e779dec1d8a527dccabaaaebda1a1 872 | fb9dd15e17d90954aca061a11093054434e513c9 873 | a6d29658849c12f7d9457f242e5055cde5bb7ccd 874 | 6aadd3c8fd9d63dfe998c699bc631b75a2e25bb3 875 | 2cd69f9f5fce5c57bd0ab4bfaf2b5b6da147fa53 876 | f8030d3f88ae69a83316cac919e803bcd67c7fd2 877 | baef0af110a328fb7bca508a9d6806914ac177a2 878 | e704a55c501333ff5b8ef890b8072b773a7c5e3c 879 | d5ca7fe029b9010f8a0ac44efdd415f2da369357 880 | b2c4c0beac8555d56001631ed61d2983f08c241f 881 | 06a6be0d5ee53ca29785cbf9b56c70406f6f1083 882 | 0118f0eba1113af3e8cce4599305ba2bb611eccc 883 | f88c52c69f314798ceaf39d3b2458c077b5610a3 884 | be54eec5442d8251a26d4f90811057aa42edfdc9 885 | 6c0a93995fa52f188a3f5e6afd76fc845a63bdb8 886 | fe63c9244ed6518cb71851d685443ee084a4d162 887 | ea8a5f0383b38a703c46749ed39baee8cec9eb0f 888 | 01d9906bbc614ab280c0fb6b22f1ec9254714ed2 889 | 35a01ee3cccfd35342904f5229c11f3841013b4c 890 | 4d66967ef2ce135e280ddeb73960f512fc66ba1b 891 | 7414e4aa69d5020d3f743ec3c3a55954cd398c39 892 | d6f954b458f7536605970345ceb05d7c68e9d90e 893 | 6d0fb38bcffe6d55129743b6908c6578c2beb777 894 | 938b065cd9da57ecfe99a318a78e4301a5094e3d 895 | eb49125080f3877dbc1d63d860c6ffd2cf65708b 896 | ffb0aab5745db32f2ea70467e2a9efbd2e2538a5 897 | 96ec4e06c665b7bd62cbe3d232f7c2d34016e136 898 | 0f8a24798a245d2f9035e07c71c2edfea57b2fe1 899 | 925b81093c20fdcb90fb24e72e2b17a85078266a 900 | 5836faceb62a806c7138058327e23f377b311614 901 | e0e7a64428139492cb38b9decfbac88586ea2ff2 902 | 799db782add6b635ce94c158d24a09ad61a8becc 903 | 660af45ef9f9f61256894e0d03876465c2c7dae8 904 | ff3b45730d66f5d7bcaa6281a14d5a0bdbc69677 905 | 625b7198565ad456059639ce3f631838ad49e887 906 | 2d24a510449ca1894d7de0dc2b6a01a357340c2b 907 | 8b3d5e6d515d4370ab7be4ddad55357f61e31325 908 | 228d32d11586b1a5a0b53f9ac66778a0b1dcd2c1 909 | beec12b15e49a598c3d13f1002c0443bfb09e1da 910 | 051a7ec13b47d986bbc11349e74fc90c6294ffab 911 | d0a4828be695219129032d65675e591caf895064 912 | 38aeaffb8f11e8a20f301bb01aa8af49bb3e6a8f 913 | 245e3dcb3d67858c58f9397da3b5ab306269fe40 914 | b15974d4789d7696508a37f57dc4d34b9eb927d9 915 | cb5547299ee9f3fb340ca3a67617553ac2fe6de6 916 | b6a9a2fd0ad2cd2f1dda934e302b6f9271fd3aa8 917 | fc1e09c6a2e8375f3b6866513bca7c220132fd28 918 | f1d8068dba490dca68272f7f6b2f976ca9f510f1 919 | 10e2555e4710894d5704122be38650758d08f4eb 920 | 41443e6c038c5f12ed6abf693ca23ec18266987a 921 | 444a07a80096a2e315e11f11cca7f8e51c8841ea 922 | fce4d859391591bdbe000188d72d4d8be6eb0f6b 923 | 7a728c51e0c54c9c78e8ba636dc61692172bc70e 924 | 0b66a5483a9b472920a8cf622198fefc68ea2969 925 | c26d0f90679c79d959f40c0274a8488559e2ce92 926 | dbc8d4e1af4b3996316444a4bc4aa5a346937178 927 | be36e3c03893e9f2485356261589f74368c14f64 928 | 352640e5bb647a5a3ddae240dff07c5ab919fe64 929 | 35929c1881140628ca9945b728b409ceada3af58 930 | da714ccb04cb56ff879486af70bc79850f5a6263 931 | 750b3056bfa8a027445a01224e2c67968eb5d1a6 932 | 6cb3291db7e02ef47df428a6dcd2d4b6720fa889 933 | 0e20080a919a4b7460d89d109f1e06c3f49b853d 934 | 01feab2b1f41891c10de5e9d649909617c6926c4 935 | 213cbf457af2cd1030b7967b99ec59192ca695c3 936 | ac2d092def5a94ee4a72775e726b604104fbce4e 937 | 238a476bf87bc6ddc088f2bbd00941c6275a94de 938 | 0608a70c6e9b3b61b4d107b0dd171c7e0df18fa4 939 | fac2dbe3f3b37390ea12452a94afbcbd1e954379 940 | 6aa150d1190427f12b9a03054944cdd85f7c341b 941 | f984b62685bc0436b237e7dff211b6ce9debdc38 942 | fcacd4e5aaebcb8bcc16c8139d6c2a172f52d57b 943 | 8076416826bbc53a08075b6b55b025e540e39e7f 944 | ed53cbc69992010a3cf9155433ef0a1840b6074c 945 | 136724429075fc24613b9d0e577e075ffc6383cd 946 | df33c99f46f92e6e0f52cfa9db4f2353e3930046 947 | f11f97fd238c771080cc12cfb22ad1c525df1af4 948 | 629130ef07bc8b7138ade36192503291e4d4dcd4 949 | e5a12c1f890db1ec062aa2843cf9a668e4f10559 950 | 80080c626a6cb4e774707e0eb8a2519b8bdf6cf7 951 | a49bc4139819a56dcb41cf56d4395fe724ab9762 952 | bac67050debe383e9d962b36d708ac9981c96b48 953 | 99b6fac1cd12d4ad9fe70ea41043269ab005773e 954 | d45d2d15db7ea31eeb49947d6106b7440b991783 955 | 5231a9f3362749b502900843b6d64392569e420c 956 | 8d0626ede2021ed61e5866067c46e863ef60af74 957 | 2e43ea1746ecd025ea8e6377ba96de1f196a0c13 958 | d05245d225ae515cf5abd14ae1f6499d88defb84 959 | 137c781f2809ee83e04765548252fc74234b7530 960 | 190864a4f16e3a53a25b4254f5957cc6186179b3 961 | 3cad9b166329970c99822433b0a8331d7fd43462 962 | f0b5fbd0bf12237182492b89e224783e394d1965 963 | baadeaf2ef7f4237caa246cd123db8e324038a7a 964 | f001bce846409740bd0f418331cff78541babff7 965 | a4b1e8cd6f03cca48bf823f08dec26b3a215122e 966 | f214ab5ac7406065e936dfde277723756293c32e 967 | a719dfaf7ea9a4a70c8f199c408ba62a70a3f599 968 | 98174a01d03433c65aa14e33e407bd968160fbaf 969 | 3967d65328c52389193ee643be21e7d020a5c73a 970 | 7d20660fb6be92f51628bd34acf75a5d5ca5ef8e 971 | b008604b7aa1e9c56d2813adab34ff3679d4ac31 972 | 5ddef9b85f56210d41f52b58c6912593287ef6e7 973 | 0d7ea968ba14cd47b453f04d175f256dd7cb59f9 974 | 7bd425f88eaa29f0c15ee22bd2869f82de191a09 975 | 14fc9e536179555b72c94136ef3f9bb77043e3e0 976 | 9d78fe7d104d831b646a2955b8120cffd29c3832 977 | 0606972bcc51b9fb39948d9e75971b52cfc2a0a4 978 | 24ec8bccf9e33dcc9356cb445591a5ce04969981 979 | 6d1de8f006339b596f8734c164ff8cbb4bcfba21 980 | 551447a02108cbf57ca962249a4d114d47f12ad3 981 | 7afa3b687e58d3f16feccb8244b90a87a535b85c 982 | d6f861c2b819b2cf7ca443beb36fe84d0a65fc1a 983 | ae36d1c17efcf754c9db87c03635f4ec3d80cb75 984 | 0f844a30d147e3c2be8e99fcc75ee6cff47465db 985 | d566f9802e480312c6661bc2804b31850777c8e0 986 | 7f42dbb0e99f763d3d65c0a8691b6090866df333 987 | 9027a516bde1234e92ee51ce901f218af74327b5 988 | 4450fbe22ffc75452f3fcfdad98e7f8978086670 989 | aca51b23bb2b0afa2f77b880eaa853c93730b34f 990 | 9ca59e5e52afd27be580eb3938d9ecddc52d9c99 991 | dca137ac5ffc5bc1f5fca714a1c8c0af7dc1e03a 992 | e3e90e1d0009727d1b8f73431f919ac59a332f42 993 | d91f61334bafbe7bb705fb944fa3fafc0b673ac3 994 | 297f0681bf8f203b5b865f1039cb8563de99897f 995 | 28ff5ed10a6dea9de26068cfc7dc8aa362ac236f 996 | 1aa1dee35a89e490510d197f477eefddfdef2073 997 | 43cfd9eedc9f4696ce277e69ba212a92ccbb40a3 998 | 459efe4f5e463698a039a043529a44b8a99f685c 999 | 1795b4588e40bddb044c77d7b5f71bc4e6e30c90 1000 | 8db762f98b0ff619d546798b6a393477695bdaad 1001 | d24524858712c3cd084c8bd543c6aa0081e16bd6 1002 | 8212e4b30dd00fd57615a812c9714108d29db5e3 1003 | 3aa3882d29bcf6937a40036d70b65ace680cee07 1004 | 99e2d448654033b1398d0d79bb47589dfc2752a1 1005 | 5c64bae58242614289fbafd2c577daf5a2c7d03d 1006 | f922ccc6b6e37a565a1424b0f99b86d89910e115 1007 | 353ef7a905077061c4f216d518c490e1b094c881 1008 | 3656246be4b14a2e05defb3b9be2e643f2224759 1009 | 00a9c147cff7f7fb72300af86a28c4ee31de3d90 1010 | 07cf1f64fe2c7d18a930d751ad71d6b13d22e969 1011 | 87ad2283193d464134771e0cf27380711c7591a8 1012 | 602425a19c8387860c6bb39e53209ba553487149 1013 | ad3b551ced248a33bc8787c9cf2cce58476744af 1014 | f6709398b1eb787ce6de38d48d66e79330852cb9 1015 | d264f5ad0c0148b922d6b30fabcd6465a937799d 1016 | 429f5b32e44e111a2121c4b72e2bffafdd4d9123 1017 | d6fcf7492a384e469119ba802c52b5de8ef32d7f 1018 | f620cd6e3fe9ac6f2d5ccb48f75cedddcfbcf959 1019 | 32e5ec4ab3c431a7947675d21ae01cc0d6e1f992 1020 | 6d492f5755e327a3d137597f606f72e92ef50bec 1021 | 4d535529a58f15b8fc436f127d573fbb6b5ad190 1022 | 9316b684d6e03742463f784fa87e537c8b6bf610 1023 | f3d659ce0b89318a3a12890e2c5825049d0eaf90 1024 | e7fefa91d45301eaf10c8762f12412c3d14e4f73 1025 | f03aff7b4ff25a79a6e326eb4391e62e67381965 1026 | fedeb6d478bea87671a0dd7914ffe9b50a4ab737 1027 | ed7ad1431ec7a19b899e8fe6a8fce6b86ba7f814 1028 | 16956cb9efc143e40743faf1bb383dab48a1b0dd 1029 | adfcd1ebf6ebd8787e30f2c9153cb4d3a45be7d1 1030 | 0bf4324cff254ffe0050907f2fc3fc7d251f3694 1031 | 701a1e8a449d984b996d8cce02b02d37f97993c3 1032 | dfb4c2f38e9e6c11ac54329dc65e8f9e1665aee6 1033 | 69bc5b1add178fec3f6b815bd8d7743bfe63697b 1034 | 6874a32c15625bbde6478de9b6f215dd7d42c787 1035 | d3b15e7d1eec7a8df94f6935d25cb5c0c6ab1e82 1036 | bdce28379ae5aca3a86ef8bc2467283416f5c830 1037 | d6b39c02ccd5ba105178c1d5aea4586c3e2c8d66 1038 | 5b95d9f5ebdbe9e3ef78d662a99edfc77554d60d 1039 | 579e419e93c2c1279ddb0f77b5fc2926a684aa82 1040 | bd57ea7ed6c33ec43b305b043b5040b9f1059a0c 1041 | 1204cc7cdcfa39c7066b7e4f4c2dade906095f79 1042 | ad64603049ac577abe9f50216ee0db6f32067c48 1043 | f13b49a75ef103167bc5059287c06c1e28aed612 1044 | eaf607adc3f689029c5b7536f30f7c4f8911c3c5 1045 | ce878c2e11a8c49aab001a5336e93a9f609386e0 1046 | 31a25836b9f2ece238511e66f6792fc010fb815d 1047 | a2f87833bac738507e0c5a9e63e6ffa21957fdb3 1048 | 1312cfc3bf5508f3e640c7fed265fef2be60f2eb 1049 | e714ac30f48ce6e33122c79493c503fbc7f51c73 1050 | 18a06cb43a9571650e9ae8d5815ddcd5d15b3aa1 1051 | ca1b44cddcbdc9933cf6716c069b9f8bc0fb7a42 1052 | 51a7f43452ce750703107cc7922318894db0f4f7 1053 | fe99dc15f11dc25dd8cfcdd91172bc6ad5e2749f 1054 | 54786152b0638bc79c97fd8a100ca7dd29c535ef 1055 | c25c6e9eb66040286f1226dcbeb9b8555f6ecaed 1056 | bcc1facaf5aee5954829daa06654de778801a902 1057 | 62c431e70fdfd4f1ac2771725812ffa5d4c1a3a7 1058 | 0f398e126d55c175f8961a4426edbbdc349b7d41 1059 | aebf661414310e76c6f6e3fc13498d0ecfbedcaa 1060 | 082e8616dd13959dd860598ace338562f42d8670 1061 | 1ff1875dccf008337370c8f23f6ca37dcbc114d9 1062 | f2d10cad93caee844c8852a8347069a4c1123905 1063 | a91488d214cd31b2e608d085e92e359ec52f2e8c 1064 | 7a705a7ba54eee46ee7450eee775ade531b6adbc 1065 | 3d04e532e4d05b4d29860b75128b07367922f727 1066 | 9cd89585869f7b4eb06e09a5a6377b316d1b7003 1067 | af28d08d9f93b24cf58a399490db48140e97a73a 1068 | 7d6434e0193c3168ea9040d0706d960136b2fae5 1069 | 8d940736aae07fb4df7865b1e163d7bcd4112ce0 1070 | 47d814078bd316c8a4c7b0a1f8dfec1833e97b7e 1071 | 0c1a6c3d019c18ecacc63427d6badaeff7fedcf2 1072 | d3435c566aa5332bf00ee45fdf77c2b16ed5c45b 1073 | 1656c197e408b6597e14325b1bed2a29092927b8 1074 | cb9b34383c51b5d96987cabe7b822ed3f3883baa 1075 | 1d4b40b9cab26736a7fe800f649ce0375318c1b4 1076 | f907d56acdc5361a2ec5b61660cb6dc92d03c0c9 1077 | 894020f9f41601339679b5c297a5e26c90207fad 1078 | a1ec8e08886c0e22ca7509e0775aebc1e8372a63 1079 | aa86c5b366f9b611311131a570efc3e0baf526e4 1080 | b360f8acf04412f7bf6e9c0ed3fce80d5ab01c40 1081 | 5ef7ef16702e095ddb7e0b558da2ecb22c06d4c9 1082 | --------------------------------------------------------------------------------