比特币地址生成

btc_gen_addr.jpg

PubKeyToAddr.png

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259

#!coding:utf8

#author:yqq
#date:2019/3/4 0004 14:35
#description: 比特币地址生成算法

import hashlib
import ecdsa
import os


#2019-05-15 添加私钥限制范围
g_b58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'

#g_nMaxPrivKey = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140 - 0x423423843 #私钥最大值 (差值是自定义的)
#g_nMinPrivKey = 0x0000000000000000000000000000000000000000000000000000000000000001 + 0x324389329 #私钥最小值 (增值是自定义的)

#2019-11-12 根据官方定义修改 有限域
# http://www.secg.org/sec2-v2.pdf#page=9&zoom=100,0,249
# 关于 有限域的定义 请参考
# 0xEFFFFFC2F = 2**32 - 2**9 - 2**8 - 2**7 - 2**6 - 2**4 - 1
g_nFactor = 0xEFFFFFC2F + 0x23492397 #增值自定义
g_nMaxPrivKey = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140 - g_nFactor #私钥最大值 (差值是自定义的)
g_nMinPrivKey = 0x0000000000000000000000000000000000000000000000000000000000000001 + g_nFactor #私钥最小值 (增值是自定义的)



def Base58encode(n):
'''
base58编码
:param n: 需要编码的数
:return: 编码后的
'''
result = ''
while n > 0:
result = g_b58[n % 58] + result
n /= 58
return result


def Base256decode(s):
'''
base256编码
:param s:
:return:
'''
result = 0
for c in s:
result = result * 256 + ord(c)
return result


def CountLeadingChars(s, ch):
'''
计算一个字符串开头的字符的次数
比如: CountLeadingChars('000001234', '0') 结果是5
:param s:字符串
:param ch:字符
:return:次数
'''
count = 0
for c in s:
if c == ch:
count += 1
else:
break
return count


def Base58CheckEncode(version, payload):
'''

:param version: 版本前缀 , 用于区分主网 和 测试网络
:param payload:
:return:
'''
s = chr(version) + payload
checksum = hashlib.sha256(hashlib.sha256(s).digest()).digest()[0:4] #两次sha256, 区前4字节作为校验和
result = s + checksum
leadingZeros = CountLeadingChars(result, '\0')
return '1' * leadingZeros + Base58encode(Base256decode(result))


def PrivKeyToPubKey(privKey):
'''
私钥-->公钥
:param privKey: 共65个字节: 0x04 + x的坐标 + y的坐标
:return:
'''
sk = ecdsa.SigningKey.from_string(privKey.decode('hex'), curve=ecdsa.SECP256k1)
# vk = sk.verifying_key
return ('\04' + sk.verifying_key.to_string()).encode('hex')

def PrivKeyToPubKeyCompress(privKey):
'''
私钥-->公钥 压缩格式公钥
:param privKey: ( 如果是奇数,前缀是 03; 如果是偶数, 前缀是 02) + x轴坐标
:return:
'''
sk = ecdsa.SigningKey.from_string(privKey.decode('hex'), curve=ecdsa.SECP256k1)
# vk = sk.verifying_key
try:
# print(sk.verifying_key.to_string().encode('hex'))
point_x = sk.verifying_key.to_string().encode('hex')[ : 32*2] #获取点的 x 轴坐标
point_y = sk.verifying_key.to_string().encode('hex')[32*2 : ] #获取点的 y 轴坐标
# print("point_x:", point_x)

if (long(point_y, 16) & 1) == 1: # 如果是奇数,前缀是 03; 如果是偶数, 前缀是 02
prefix = '03'
else:
prefix = '02'
return prefix + point_x
except:
raise("array overindex")
pass



#https://en.bitcoin.it/wiki/List_of_address_prefixes
def PubKeyToAddr(privKey, isTestnet = False):
'''
公钥-->地址
:param privKey:私钥
:param isTestnet:是否是测试网络
:return:地址
'''
ripemd160 = hashlib.new('ripemd160')
ripemd160.update(hashlib.sha256(privKey.decode('hex')).digest())
if isTestnet:
return Base58CheckEncode(0x6F, ripemd160.digest()) #0x6F p2pkh testnet
# return base58CheckEncode(0x05, ripemd160.digest()) #05 p2sh mainnet
return Base58CheckEncode(0x00, ripemd160.digest()) #00 p2pkh mainnet




def PrivKeyToWIF(privKey, isTestnet = False):
'''
将私钥转为 WIF格式 , 用于比特币钱包导入
:param privKey: 私钥(16进制字符串)
:return: WIF格式的私钥
'''
if isTestnet:
# return Base58CheckEncode(0xEF, privKey.decode('hex') + '\01') #0xEF 测试网络 fix bug: 2019-04-03 yqq 01是多余的, 只有是压缩的格式的时候,才需要加
return Base58CheckEncode(0xEF, privKey.decode('hex') ) #0xEF 测试网络
# return Base58CheckEncode(0x80, privKey.decode('hex') + '\01') #0x80 主网
return Base58CheckEncode(0x80, privKey.decode('hex') ) #0x80 主网

def PrivKeyToWIFCompress(privKey, isTestnet = False):
'''
压缩格式
将私钥转为 WIF格式 , 用于比特币钱包导入
:param privKey: 私钥(16进制字符串)
:return: WIF格式的私钥
'''
if isTestnet:
return Base58CheckEncode(0xEF, privKey.decode('hex') + '\01') #0xEF 测试网络
return Base58CheckEncode(0x80, privKey.decode('hex') + '\01') #0x80 主网


def GenPrivKey():
'''
生成私钥, 使用 os.urandom (底层使用了操作系统的随机函数接口, 取决于CPU的性能,各种的硬件的数据指标)
:return:私钥(16进制编码)
'''

#2019-05-15 添加私钥范围限制
while True:
privKey = os.urandom(32).encode('hex') #生成 256位 私钥
if g_nMinPrivKey < int(privKey, 16) < g_nMaxPrivKey:
return privKey


def GenAddr(isTestnet=False):
'''
此函数用于C++调用,
:param isTestnet: 是否是测试网络
:return: (私钥, 公钥, 地址)
'''
privKey = GenPrivKey()
# print("privkey : " + privKey)
privKeyWIF = PrivKeyToWIF(privKey, isTestnet)
# print("privkey WIF:" + PrivKeyToWIF(privKey, isTestnet))
pubKey = PrivKeyToPubKey(privKey)
# print("pubkey : " + pubKey)
addr = PubKeyToAddr(pubKey, isTestnet)
# print("addr : " + addr)
return str(privKeyWIF), str(pubKey), str(addr)




def GenAddrCompress(isTestnet=False):
'''
此函数用于C++调用,
:param isTestnet: 是否是测试网络
:param isCompress: 是否压缩
:return: (私钥, 公钥, 地址)
'''
privKey = GenPrivKey()
# print("privkey : " + privKey)
privKeyWIF = PrivKeyToWIFCompress(privKey, isTestnet)
# print("privkey WIF:" + PrivKeyToWIF(privKey, isTestnet))
pubKey = PrivKeyToPubKeyCompress(privKey)
# print("pubkey : " + pubKey)
addr = PubKeyToAddr(pubKey, isTestnet)
# print("addr : " + addr)
return str(privKeyWIF), str(pubKey), str(addr)



def GenMultiAddr(nAddrCount = 1, isTestnet=True):
'''
生成多个地址
:param nAddrCount:
:param isTestnet:
:return:
'''
# return [("1111", "2222", "3333"), ("4444", "55555", "66666")]
# return [1, 2, 3, 4]
# return ["1111", "2222", "3333", "4444"]

lstRet = []
for i in range(nAddrCount):
lstRet.append(GenAddrCompress(isTestnet))
return lstRet

#
def good():

isTestnet = True


# private_key = GenPrivKey()
private_key = '95b51ad564bd26811aeafc06ebe64643d2a50f82aa4901e714ba4be635ed9a57'
print("privkey : " + private_key)
print("privkey WIF:" + PrivKeyToWIF(private_key, isTestnet))
pubKey = PrivKeyToPubKey(private_key)
print("pubkey : " + pubKey)
addr = PubKeyToAddr( pubKey , isTestnet)
print("addr : " + addr)
print("-----------------------------")
print("privkey WIF compress:" + PrivKeyToWIFCompress(private_key, isTestnet))
pubKey = PrivKeyToPubKeyCompress(private_key)
print("pubkey compress : " + pubKey)
addr = PubKeyToAddr( pubKey , isTestnet)
print("addr compress: " + addr)
#
#
# def main():
# good()
# for i in range(1):
# print(GenAddr(True))

# if __name__ == '__main__':
#
# main()

关于地址压缩

参考链接: https://bitcoin.stackexchange.com/questions/3059/what-is-a-compressed-bitcoin-key


2021评语:

以上的python实现, 可以作为理解比特币地址生成的例子

如果实际项目开发中需要进行地址生成, 可以借助一些现成的库进行地址管理, 推荐使用BIP44规范进行私钥管理

什么是BIP44: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki

比特币的一些库:

Python:

C++:

Golang:

Rust:

  • Copyrights © 2021-2024 youngqqcn

请我喝杯咖啡吧~