Skip to content
This repository was archived by the owner on Apr 11, 2022. It is now read-only.

Commit d9738b4

Browse files
committed
initial commit
1 parent 1301a0d commit d9738b4

File tree

12 files changed

+509
-0
lines changed

12 files changed

+509
-0
lines changed

CMakeLists.txt

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
cmake_minimum_required (VERSION 3.1)
2+
project (proofofwork)
3+
4+
set (CMAKE_C_STANDARD 99)
5+
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -mtune=native -march=native")
6+
7+
# check AVX
8+
include(CheckCSourceCompiles)
9+
check_c_source_compiles ("#include <immintrin.h>\n int main(void) { __m256i ymm = _mm256_setzero_si256(); return 0; }" HAVE_AVX)
10+
if (NOT HAVE_AVX)
11+
message (FATAL_ERROR "AVX instructions are not supported in your CPU")
12+
endif ()
13+
14+
# check OpenMP
15+
find_package (OpenMP)
16+
if (OPENMP_FOUND)
17+
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
18+
endif ()
19+
20+
include_directories (include)
21+
add_library (proofofwork SHARED
22+
src/md5-avx2.c)

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2017 Kimiyuki Onaka.
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
13+
all 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
21+
THE SOFTWARE.

benchmark/Makefile

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
.PHONY: build
2+
3+
CC ?= clang
4+
CCFLAGS += -std=c99 -Wall -O3 -mtune=native -march=native
5+
PYTHON ?= python3
6+
7+
all: openssl pyhashlib
8+
9+
proofofwork:
10+
${CC} ${CCFLAGS} -fopenmp proofofwork.c -I../include ../src/md5-avx2.c
11+
${PYTHON} report.py ./a.out
12+
13+
openssl:
14+
${CC} ${CCFLAGS} -lcrypto openssl.c
15+
${PYTHON} report.py ./a.out
16+
17+
pyhashlib:
18+
${PYTHON} report.py ${PYTHON} pyhashlib.py

benchmark/openssl.c

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#include <stdio.h>
2+
#include <stdint.h>
3+
#include <openssl/md5.h>
4+
int main(void) {
5+
uint8_t s[6+1] = {};
6+
uint8_t digest[MD5_DIGEST_LENGTH];
7+
int cnt = 0;
8+
#define repeat_ascii(c) for (c = '!'; c <= '~'; ++ c)
9+
repeat_ascii (s[0]) {
10+
repeat_ascii (s[1]) {
11+
repeat_ascii (s[2]) {
12+
repeat_ascii (s[3]) {
13+
repeat_ascii (s[4]) {
14+
repeat_ascii (s[5]) {
15+
#undef repeat_ascii
16+
MD5(s, sizeof(s)-1, digest);
17+
++ cnt;
18+
if (digest[0] == 0xc0 && digest[1] == 0xff && digest[2] == 0xee && (digest[3] & 0xf0) == 0xe0) {
19+
goto done;
20+
}
21+
}}}}}}
22+
done:
23+
printf("c0ffeee\n");
24+
printf("%s\n", s);
25+
printf("%d\n", cnt);
26+
return 0;
27+
}

benchmark/proofofwork.c

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#include <stdio.h>
2+
#include <string.h>
3+
#include "proofofwork.h"
4+
5+
int main(int argc, char **argv) {
6+
uint8_t mask[pow_md5_digest_length] = { 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
7+
uint8_t target[pow_md5_digest_length] = { 0xca, 0xfe, 0xba, 0xbe, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
8+
uint8_t buffer[pow_md5_chunk_length];
9+
uint64_t size = 15;
10+
memcpy(buffer, "libproofofwork:", size);
11+
pow_md5_mine(mask, target, buffer, &size);
12+
printf("cafebabe\n");
13+
printf("%s\n", buffer);
14+
printf("%lu\n", pow_count);
15+
return 0;
16+
}

benchmark/pyhashlib.py

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/usr/bin/env python3
2+
import hashlib
3+
import itertools
4+
for i in itertools.count():
5+
s = str(i).encode()
6+
digest = hashlib.md5(s).hexdigest()
7+
if digest.startswith('c0ffee'):
8+
break
9+
print('c0ffee')
10+
print(i)
11+
print(i)

benchmark/report.py

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#!/usr/bin/env python3
2+
import argparse
3+
import hashlib
4+
import subprocess
5+
import time
6+
import sys
7+
8+
# parse args
9+
parser = argparse.ArgumentParser()
10+
parser.add_argument('command', nargs=argparse.REMAINDER)
11+
args = parser.parse_args()
12+
13+
# run command
14+
print('[*] run: {}'.format(args.command))
15+
begin = time.time()
16+
proc = subprocess.Popen(args=args.command, stdout=subprocess.PIPE, stderr=sys.stderr)
17+
stdout, _ = proc.communicate()
18+
end = time.time()
19+
elapsed = end - begin
20+
print('[*] done: {} sec elapsed'.format(elapsed))
21+
22+
# recieve output
23+
prefix, s, cnt = stdout.splitlines()
24+
prefix = prefix.decode().lower()
25+
s = s.decode()
26+
cnt = int(cnt)
27+
assert all(c in '0123456789abcdef' for c in prefix)
28+
29+
# print result
30+
digest = hashlib.md5(s.encode()).hexdigest().lower()
31+
print('[*] md5({}) = {}'.format(repr(s), digest))
32+
if not digest.startswith(prefix):
33+
print('[ERROR] wrong result: {} for prefix {}'.format(repr(s), prefix))
34+
sys.exit(1)
35+
print('[*] accepted: {} for prefix {}'.format(repr(s), prefix, elapsed))
36+
print('[*] {} hashes/sec'.format(cnt / elapsed))
+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import sys
2+
if sys.version_info[0] < 3:
3+
import warnings
4+
warnings.warn("Python 3.x is assumed", DeprecationWarning)
5+
6+
import os
7+
import ctypes
8+
library_path = os.path.abspath(os.path.join(os.path.dirname(sys.modules['proofofwork'].__file__), 'libproofofwork.so'))
9+
library = ctypes.cdll.LoadLibrary(library_path)
10+
library.pow_md5_mine.restype = ctypes.c_bool
11+
# library.pow_md5_mine.argtypes = (ctypes.POINTER(ctypes.c_uint8), ctypes.POINTER(ctypes.c_uint8), ctypes.POINTER(ctypes.c_uint8), ctypes.POINTER(ctypes.c_uint64))
12+
13+
MD5_DIGEST_LENGTH = 16
14+
MD5_CHUNK_LENGTH = 64
15+
prefix_length_limit = 44
16+
def md5(s, prefix=None): # str, bytes -> bytes
17+
if not isinstance(s, str):
18+
raise TypeError
19+
if len(s) > 2*MD5_DIGEST_LENGTH:
20+
raise ValueError
21+
s += '?' * (2*MD5_DIGEST_LENGTH - len(s))
22+
if not all(c in '0123456789abcdef?' for c in s):
23+
raise ValueError
24+
if prefix is None:
25+
prefix = b''
26+
if not isinstance(prefix, bytes):
27+
raise TypeError
28+
if len(prefix) > prefix_length_limit:
29+
raise ValueError
30+
mask = (ctypes.c_uint8 * MD5_DIGEST_LENGTH)()
31+
target = (ctypes.c_uint8 * MD5_DIGEST_LENGTH)()
32+
for i in range(MD5_DIGEST_LENGTH):
33+
a = 0
34+
b = 0
35+
if s[i*2] != '?':
36+
a |= 0xf0
37+
b += int(s[i*2], 16) * 0x10
38+
if s[i*2+1] != '?':
39+
a |= 0x0f
40+
b += int(s[i*2+1], 16)
41+
mask[i] = a
42+
target[i] = b
43+
buf = (ctypes.c_uint8 * MD5_CHUNK_LENGTH)()
44+
for i, c in enumerate(prefix):
45+
buf[i] = c
46+
size = ctypes.c_uint64(len(prefix))
47+
found = library.pow_md5_mine(ctypes.byref(mask), ctypes.byref(target), ctypes.byref(buf), ctypes.byref(size))
48+
if found:
49+
return bytes(buf[:size.value])
50+
else:
51+
return None

bindings/python/setup.py

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#!/usr/bin/env python3
2+
from distutils.core import setup
3+
from distutils.command.sdist import sdist
4+
from distutils.command.build_clib import build_clib
5+
import os
6+
import sys
7+
import subprocess
8+
import shutil
9+
10+
class custom_sdist(sdist):
11+
def run(self):
12+
# copy files to somewhere whose paths don't contain "../", and included via MANIFEST.in
13+
srcfiles = []
14+
srctrees = []
15+
srcfiles += [ '../../CMakeLists.txt' ]
16+
srctrees += [ '../../src' ]
17+
srctrees += [ '../../include' ]
18+
for src in srcfiles:
19+
dst = os.path.join('proofofwork', os.path.basename(src))
20+
if os.path.exists(dst):
21+
os.remove(dst)
22+
print('copying {} -> {}'.format(src, dst))
23+
shutil.copy2(src, dst)
24+
for src in srctrees:
25+
dst = os.path.join('proofofwork', os.path.basename(src))
26+
if os.path.exists(dst):
27+
shutil.rmtree(dst)
28+
print('copying {} -> {}'.format(src, dst))
29+
shutil.copytree(src, dst)
30+
return sdist.run(self)
31+
32+
class custom_build_clib(build_clib):
33+
def build_libraries(self, libraries):
34+
# workaround
35+
subprocess.check_call([ 'cmake', 'proofofwork', '-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=build/lib/proofofwork' ], stdout=sys.stdout, stderr=sys.stderr)
36+
subprocess.check_call([ 'make' ], stdout=sys.stdout, stderr=sys.stderr)
37+
38+
setup(
39+
name='proofofwork',
40+
version='0.0.1',
41+
description='', # TODO
42+
# long_description=readme,
43+
author='Kimiyuki Onaka',
44+
author_email='[email protected]',
45+
url='https://github.com/kmyk/', # TODO
46+
license='MIT License',
47+
packages=[ 'proofofwork' ],
48+
cmdclass=dict(
49+
sdist=custom_sdist,
50+
build_clib=custom_build_clib,
51+
),
52+
package_data={
53+
'proofofwork' : [
54+
'CMakeLists.txt',
55+
'src/*.c',
56+
'include/*.h',
57+
],
58+
},
59+
libraries=[(
60+
'proofofwork', dict(
61+
package='proofofwork',
62+
sources=[], # dummy
63+
),
64+
)],
65+
classifiers=[
66+
'Environment :: Console',
67+
'Intended Audience :: Developers',
68+
'License :: OSI Approved :: MIT License',
69+
'Operating System :: OS Independent',
70+
'Programming Language :: Python :: 3',
71+
],
72+
)

include/proofofwork.h

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
enum { pow_md5_chunk_length = 64 };
2+
enum { pow_md5_digest_length = 16 };
3+
/**
4+
* @param [in] mask the length must be pow_md5_digest_length.
5+
* @param [in] target the length must be pow_md5_digest_length.
6+
* @param [in,out] buffer the length must be pow_md5_digest_length.
7+
* @param [in,out] size input: must be <= 44, buffer[0, size) is used as a prefix of texts.
8+
* @return whether a text is found or not.
9+
*/
10+
bool pow_md5_mine(uint8_t *mask, uint8_t *target, uint8_t *buffer, uint64_t *size);

readme.md

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# libProofOfWork
2+
3+
Simple hash-mining c library and its python binding.
4+
5+
## Todo
6+
7+
- write test
8+
- support SHA1, SHA2
9+
- support old CPU
10+
- support GPU
11+
12+
## License
13+
14+
MIT License

0 commit comments

Comments
 (0)