如何破解世界圆周率日OKX的私钥谜题大奖(附带完整代码)

Author: 发明者量化, Created: 2023-03-14 23:25:45, Updated: 2023-09-18 20:08:33

img

看隔壁大神群里说OKX在搞世界圆周率日解秘活动, 主题如下

img

做为一个资深老码农,看到消息后, 嘴角微微上扬, 打开我的MacBook Pro, 啥也不说了,搞起

分析

官方说了,有61位密钥在图片中与圆周率重合的部分, 私钥大家都知道长度是32 byte, 转换成hex也就是64位再加上0x前缀, 一共66位,已经有61位了,肉眼一看图里面第一行的"0X"明显不是圆周率是私钥开始前缀, 那就还有5位右(0123456789ABCDEF)这些字符的随机排列, 暴力跑没啥问题, 开始动手

用Mac自带的工具简单的锐化调色处理下

img

大概这个样子, 然后Mac是可以自动识别图片上的字符的,咱们直接复制:

3.141592653589793230X1D64338
А694502884197169399375105820
974925E123078164062862089986
28033DB034211706409914808651
32823066470ED424609550582231
8B3
81284
•探索,
038
永无止境
027
493
05%
0128
4756482337867831731712019091
47D9E56692346034861045432664
8213393607743749141273724587
006606315588174881BEEA209628
2925409192744436789259036001

这个样子肯定不能用,咱们人工修正下, 被图遮挡的不确认的咱们标记成*号,不太确定,后面再说 其它被图挡住的,可以通过观察其它字体的笔画形态来判断出来, 这时候我们上Python,计算两个之间的差异,相同的用’_'表示,只显示不同的

img = '''
3.141592653589793230X1D64338
A694502884197169399375105820
974925E123078164062862089986
28033DB034211706409914808651
32823066470ED424609550582231
8B32594081284811174502841027
0193**2*D2299964462294895493
0381960EFC8103F9365933446128
4756482337867831731712019091
47D9E56692346034861045432664
82133936077A3749141273724587
006606315588174881BEEA209628
2925409192744436789259036001
'''
# 真实圆周率
real='''
3.14159265358979323846264338
3279502884197169399375105820
9749445923078164062862089986
2803482534211706798214808651
3282306647093844609550582231
7253594081284811174502841027
0193852110555964462294895493
0381964428810975665933446128
4756482337867831652712019091
4564856692346034861045432664
8213393607260249141273724587
0066063155881748815209209628
2925409171536436789259036001
'''

items = img.strip().split('\n')
diffStr = ''
for pos, line in enumerate(real.strip().split('\n')):
    for i, c in enumerate(line):
        imgLine = list(items[pos])
        if line[i] == imgLine[i]:
            imgLine[i] = '_'
        else:
            diffStr += imgLine[i]
        items[pos] = ''.join(imgLine)
print('\n'.join(items))
print(diffStr, 'Len:', len(diffStr))

执行结果如下:

___________________0X1D_____
A694________________________
____25E1____________________
____3DB0________4099________
___________ED42_____________
8B32________________________
____**_*D2299_______________
______0EFC___3F93___________
________________731_________
_7D9E_______________________
__________7A37______________
__________________BEEA______
________92744_______________
0X1DA69425E13DB04099ED428B32***D22990EFC3F937317D9E7A37BEEA92744 Len: 64

多出来3位, 刚好里面有3个不确定的,把他去掉试试吧, 因为排除来看其它的61位都是没问题的 只留最确定的差异,最终理到前缀如下

0X1DA69425E13DB04099ED428B32D22990EFC3F937317D9E7A37BEEA92744

下面还是要上Python,暴力去OK链上爬余额,比较哪个私钥有余额就是哪个了, 其实也可以先选出来有314USDT转账的公钥,这样更快, 看信息官网说留的有gas那就直接查余额好了, 代码比较乱

import sys
import web3,time,logging
from eth_account import Account
from web3 import Web3
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s')
w3 = Web3(Web3.HTTPProvider("https://exchainrpc.okex.org"))
logging.info(w3.clientVersion)
found = None

def get_balance_gas(key):
    global found
    _counter += 1
    address = Account.from_key(key).address
    logging.info('fetch address %s %s' % (found, address))
    while True:
        try:
            balance = w3.eth.get_balance(address)
            break
        except:
            logging.warning(traceback.format_exc())
            time.sleep(1)
            continue
    if balance != 0:
        found = key
        raise BaseException('Found balance: %s %s' % (address, balance))
    return balance

from concurrent.futures import ThreadPoolExecutor
executor = ThreadPoolExecutor(max_workers=50)
keys = []
prefix = '1DA69425E13DB04099ED428B32D22990EFC3F937317D9E7A37BEEA92744'
# 无所谓优化算法了, 争分夺秒跑密钥
ch = '0123456789ABCDEF'
for a in range(0, 16):
    for b in range(0, 16):
        for c in range(0, 16):
            for d in range(0, 16):
                for e in range(0, 16):
                    keys.append("0x"+prefix+ch[a]+ch[b]+ch[c]+ch[d]+ch[e])
print('all keys:', len(keys))
tasks = [None for t in keys]
for idx, key in enumerate(keys):
    tasks[idx] = executor.submit(get_balance_gas, key)
for t in tasks:
    t.result()
    

可以看到需要遍历1048576次,用多线程速度快好多, 放我的一台服务器上跑

2023-03-15 00:20:19,491 exchain-v1.6.8.5
all keys: 1048576
2023-03-15 00:20:20,372 fetch address None 0xc20C41f06F2428a0FD84ef03Ec6960992F5f8016
2023-03-15 00:20:20,473 fetch address None 0xcFa87ee54bc1c14c09a3AB3f41640bBab5C5480a
2023-03-15 00:20:20,483 fetch address None 0x126E922652f8B276F231D0eCA94d98685a26a45D
以下省略......

焦急的等待中~~~~,不好的消息来了, 策略退出前也没结果,都是显示的None

峰回路转

不应该啊, 百思不得其解, 这时候开始去twitter上看大家的讨论,发现大家跟我的到达的步骤都差不多,但奇怪的是,这时候还没有人说解密成功, 官方也没有公布正确的前61位,这时候一个奇怪地回帖引起了我的注意,因为中间有个差异D2299是没有任何异义的, 我很确定的, 但他也好自信的样子.

img

但这个人发贴子给出的61位却是D2290, 他还说是仔细核对过的, 无所谓,碰碰运气吧, 反正也注是浪费点电

img

抱着尝试的心态, 我又找到了好几个其它人留言说找到的61位, 大家都很自信的样子.

img

把评论区找到的前缀都收集了下,用刚的代码遍历下,基本代码没啥改动,不重复贴

prefixs =[
'1DA69425E13DB04099ED428B3202290EFC3F9317317D9E7A37BEEA92744',
'1DA69425E13DB04099ED428B32D2290EFC3F9373177D9E7A37BEEA92744',
'1DA69425E13DB04099ED428B320D2290EFC3F937317D9E7A37BEEA92744',
'1DA694255E3DB040990ED428B3208890EFC3F937317D9E7A37BEEA92744',
'1DA69425E13DB04099ED428B3202299EFC3F9317317D9E7A37BEEA92744',
'1DA69425E13DB01099ED428B3202290EFC3F9317317D9E7A37BEEA92744',
'1DA69425E13DB04099ED428B32D2290EFC3F9317317D9E7A37BEEA92744',
'1DA69425E13DB04099ED428B32D22990EFC3F937317D9E7A37BEEA92744',
]

其实后面还有更多新留言, 有个兄弟的PI值还跟我的不一样,很神奇 先用这几个跑吧,不敢相信自己的眼睛, 具然跑出来了,说明上面前缀里面一个是正确的, 这很不科学, 私钥是:

1DA69425E13DB04099ED428B32D2290EFC3F9373177D9E7A37BEEA92744C8155

他这里是D229, 但图片很清楚是D2299, 而且731后面是一个7, 他这里是两个, 但居然这是正确的61位, 他自己如何计算的不得而知,无比好奇,但我对自己的操作也不怀疑, 看评论区说官方客服说圆周率有另一个版本, 我孤陋寡闻了, 以我所学习的知识以及人类目前对宇宙的理解, 圆周率这个无理数可以代表着宇宙的无限, 它的小数部分永远不会重复或终止, 你可以把他理解为你所在这个宇宙的UUID, 如果有其它版本, 可能来自平行世界吧.

这是另一个大兄弟表示质疑后客服的回答:

img

算是涨知识了

Web3转币代码

最后是把奖金转走的代码,这次我直接用的FMZ平台的Web3功能, 链地址设置成OKC就行, 私钥添加交易所的时候随意就行反正用不上, 然后两行就搞定了, 直接在调试工具模式执行

img

证明

破解后的公钥地址

>>> from eth_account import Account
>>> Account.from_key('0x1DA69425E13DB04099ED428B32D2290EFC3F9373177D9E7A37BEEA92744C8155').address
'0x0bd08825e05e540C9508961a32E58D14da47275A'

查看链接: https://www.okx.com/cn/explorer/okc/address/0x0bd08825e05e540c9508961a32e58d14da47275a

被我领走后的TX: https://www.okx.com/cn/explorer/okc/tx/0x4211418b09571011417257201aaf10fc3c5d638809a9456eb5aba5fe8c5d4e2c

可以看到接收方地址为

0x25f0a126be95f437ee71d7c4de725567c5f6c731

img

为了证明这个地址是我的,我用这个接收方的地址,给一个黑洞地址转了一次账

https://www.okx.com/cn/explorer/okc/tx/0xc32b5e299064456af3eb67c34a3b153f74a1bd18a31429052e3e3c5614bcdb6e

黑洞地址为 0x0000000000000000005757572e464d5a2e434f4d

这个黑洞地址包含一个网址解码后内容为

~ % python -c 'print(bytes.fromhex("0000000000000000005757572e464d5a2e434f4d"))'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00WWW.FMZ.COM'

后话

到这里就结束了,回过头来,还是感谢自己这么多年的知识积累,不然破解速度没这么快,会被其它人抢先一步 很确定是官方的图有失误, 总之,还是比较幸运的在答案公开前破译, 希望OKX下次举办类似活动时更加严谨.


Related

More

Dady 104万次得花多少时间

Johnny Z大牛逼!!!

btcrobot 动手能力一流

奥克量化 Z大牛逼!!!

发明者量化 20分钟左右

发明者量化 老铁来了