openssl示例
btc地址官方文档
cryptotools项目地址
看了这篇有关btc address的文章,想在shell下检验下,主要是想看下sha256的结果在linux下如何得到的
ASCII编码:
ASCII第一次以规范标准的型态发表是在1967年,最后一次更新则是在1986年,至今为止共定义了128个字符,其中33个字符无法显示(这是以现今操作系统为依归,但在DOS模式下可显示出一些诸如笑脸、扑克牌花式等8-bit符号),且这33个字符多数都已是陈废的控制字符,控制字符的用途主要是用来操控已经处理过的文字,在33个字符之外的是95个可显示的字符,包含用键盘敲下空白键所产生的空白字符也算1个可显示字符(显示为空白)。
ASCII控制字符
二进制 | 十进制 | 十六进制 | 缩写 | 可以显示的表示法 | 名称/意义 |
---|---|---|---|---|---|
0000 0000 | 0 | 00 | NUL | ␀ | 空字符(Null) |
0000 0001 | 1 | 01 | SOH | ␁ | 标题开始 |
0000 0010 | 2 | 02 | STX | ␂ | 本文开始 |
0000 0011 | 3 | 03 | ETX | ␃ | 本文结束 |
0000 0100 | 4 | 04 | EOT | ␄ | 传输结束 |
0000 0101 | 5 | 05 | ENQ | ␅ | 请求 |
0000 0110 | 6 | 06 | ACK | ␆ | 确认回应 |
0000 0111 | 7 | 07 | BEL | ␇ | 响铃 |
0000 1000 | 8 | 08 | BS | ␈ | 退格 |
0000 1001 | 9 | 09 | HT | ␉ | 水平定位符号 |
0000 1010 | 10 | 0A | LF | ␊ | 换行键 |
0000 1011 | 11 | 0B | VT | ␋ | 垂直定位符号 |
0000 1100 | 12 | 0C | FF | ␌ | 换页键 |
0000 1101 | 13 | 0D | CR | ␍ | 归位键 |
0000 1110 | 14 | 0E | SO | ␎ | 取消变换(Shift out) |
0000 1111 | 15 | 0F | SI | ␏ | 启用变换(Shift in) |
0001 0000 | 16 | 10 | DLE | ␐ | 跳出数据通讯 |
0001 0001 | 17 | 11 | DC1 | ␑ | 设备控制一(XON 启用软件速度控制) |
0001 0010 | 18 | 12 | DC2 | ␒ | 设备控制二 |
0001 0011 | 19 | 13 | DC3 | ␓ | 设备控制三(XOFF 停用软件速度控制) |
0001 0100 | 20 | 14 | DC4 | ␔ | 设备控制四 |
0001 0101 | 21 | 15 | NAK | ␕ | 确认失败回应 |
0001 0110 | 22 | 16 | SYN | ␖ | 同步用暂停 |
0001 0111 | 23 | 17 | ETB | ␗ | 区块传输结束 |
0001 1000 | 24 | 18 | CAN | ␘ | 取消 |
0001 1001 | 25 | 19 | EM | ␙ | 连接介质中断 |
0001 1010 | 26 | 1A | SUB | ␚ | 替换 |
0001 1011 | 27 | 1B | ESC | ␛ | 跳出 |
0001 1100 | 28 | 1C | FS | ␜ | 文件分割符 |
0001 1101 | 29 | 1D | GS | ␝ | 组群分隔符 |
0001 1110 | 30 | 1E | RS | ␞ | 记录分隔符 |
0001 1111 | 31 | 1F | US | ␟ | 单元分隔符 |
0111 1111 | 127 | 7F | DEL | ␡ | 删除 |
ASCII可显示字符
|
|
|
xxd:理解下来就是把文件内容或文本内容转换为ASCII码格式。
xxd命令可以为给定的标准输入或者文件做一次十六进制的输出,它也可以将十六进制输出转换为原来的二进制格式。这也有助于对任意文件的编码和解码。
TLDR中输出的xxd的用法:
xxd
Create a hexadecimal representation (hexdump) from a binary file, or vice-versa.
More information: https://manned.org/xxd.
- Generate a hexdump from a binary file and display the output:
xxd input_file
- Generate a hexdump from a binary file and save it as a text file:
xxd input_file output_file
- Display a more compact output, replacing consecutive zeros (if any) with a star:
xxd -a input_file
- Display the output with 10 columns of one octet (byte) each:
xxd -c 10 input_file
- Display output only up to a length of 32 bytes:
xxd -l 32 input_file
- Display the output in plain mode, without any gaps between the columns:
xxd -p input_file
- Revert a plaintext hexdump back into binary, and save it as a binary file:
xxd -r -p input_file output_file
文章中提到的16进制私钥:0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d
xxd的一些试验
对文本的操作:
1、直接执行xxd
[vagrant@vm-node1:btc]$ echo -n "0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d" | xxd
00000000: 3063 3238 6663 6133 3836 6337 6132 3237 0c28fca386c7a227
00000010: 3630 3062 3266 6535 3062 3763 6165 3131 600b2fe50b7cae11
00000020: 6563 3836 6433 6266 3166 6265 3437 3162 ec86d3bf1fbe471b
00000030: 6538 3938 3237 6531 3964 3732 6161 3164 e89827e19d72aa1d
以上xxd对文本进行了16进制转储,包含:索引行数、每组的默认八位字节数为2,其分组大小为4字节、标准列长度为16位,带有空格,例如:3063:其中30是16进制标识,转换为10进制是48,对应的ASCII表中的0;63对应的10进制是99,对应的ASCII表中的c
2、-b | -bits
转到比特(二进制数字)模式,而不是十六进制模式。在这种模式下,每个字符被表示成八个0/1的数字, 而不是一般的十六进制形式。每一行都以一个用十六进制形式表示的行号,后面是 ascii (或ebcdic)形式开头。命令行选项-r, -p, -i在这个模式下不起作用
[vagrant@vm-node1:btc]$ echo -n "0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d" | xxd -b
00000000: 00110000 01100011 00110010 00111000 01100110 01100011 0c28fc
00000006: 01100001 00110011 00111000 00110110 01100011 00110111 a386c7
0000000c: 01100001 00110010 00110010 00110111 00110110 00110000 a22760
00000012: 00110000 01100010 00110010 01100110 01100101 00110101 0b2fe5
00000018: 00110000 01100010 00110111 01100011 01100001 01100101 0b7cae
0000001e: 00110001 00110001 01100101 01100011 00111000 00110110 11ec86
00000024: 01100100 00110011 01100010 01100110 00110001 01100110 d3bf1f
0000002a: 01100010 01100101 00110100 00110111 00110001 01100010 be471b
00000030: 01100101 00111000 00111001 00111000 00110010 00110111 e89827
00000036: 01100101 00110001 00111001 01100100 00110111 00110010 e19d72
0000003c: 01100001 01100001 00110001 01100100 aa1d
以上内容中,00110000 是48的二进制表示,对应ASCII表内的0,01100011是99的二进制表示,对应ASCII表内的c。第一列代表16进制的行号,比如:00000012是代表第18行,00000018是代表24行
3、-c cols | -cols cols
每行表示<cols>个字符。 默认 16 (-i: 12, -ps: 30, -b: 6)。 最多256,以下示例内每行32个字符
[vagrant@vm-node1:btc]$ echo -n "0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d" | xxd -c 32
00000000: 3063 3238 6663 6133 3836 6337 6132 3237 3630 3062 3266 6535 3062 3763 6165 3131 0c28fca386c7a227600b2fe50b7cae11
00000020: 6563 3836 6433 6266 3166 6265 3437 3162 6538 3938 3237 6531 3964 3732 6161 3164 ec86d3bf1fbe471be89827e19d72aa1d
4、-g bytes | -groupsize bytes
每<bytes>个字符(每两个十六进制字符或者八个二进制数字)之间用一个空格隔开。用 -g 0禁止分组。在普通模式中<Bytes>默认是2,在二进制模式中是1。分组并不适用于postscript 或者include style 选项
g=0时:
[vagrant@vm-node1:btc]$ echo -n "0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d" | xxd -g 0
00000000: 30633238666361333836633761323237 0c28fca386c7a227
00000010: 36303062326665353062376361653131 600b2fe50b7cae11
00000020: 65633836643362663166626534373162 ec86d3bf1fbe471b
00000030: 65383938323765313964373261613164 e89827e19d72aa1d
g=4时,每4个字节分一组:
[vagrant@vm-node1:btc]$ echo -n "0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d" | xxd -g 4
00000000: 30633238 66636133 38366337 61323237 0c28fca386c7a227
00000010: 36303062 32666535 30623763 61653131 600b2fe50b7cae11
00000020: 65633836 64336266 31666265 34373162 ec86d3bf1fbe471b
00000030: 65383938 32376531 39643732 61613164 e89827e19d72aa1d
如:30633238中,30对应10进制的48,在ASCII表内对应0,63对应10进制的99,在ASCII表内对应c,32对应10进制的50,在ASCII表内对应2,38对应10进制的56,在ASCII表内对应8
在二进制中时(xxd使用了-b),默认是1:
[vagrant@vm-node1:btc]$ echo -n "0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d" | xxd -b
00000000: 00110000 01100011 00110010 00111000 01100110 01100011 0c28fc
00000006: 01100001 00110011 00111000 00110110 01100011 00110111 a386c7
0000000c: 01100001 00110010 00110010 00110111 00110110 00110000 a22760
00000012: 00110000 01100010 00110010 01100110 01100101 00110101 0b2fe5
00000018: 00110000 01100010 00110111 01100011 01100001 01100101 0b7cae
0000001e: 00110001 00110001 01100101 01100011 00111000 00110110 11ec86
00000024: 01100100 00110011 01100010 01100110 00110001 01100110 d3bf1f
0000002a: 01100010 01100101 00110100 00110111 00110001 01100010 be471b
00000030: 01100101 00111000 00111001 00111000 00110010 00110111 e89827
00000036: 01100101 00110001 00111001 01100100 00110111 00110010 e19d72
0000003c: 01100001 01100001 00110001 01100100 aa1d
如:01100101对应10进制中的101,在ASCII表内对应的是字母e
5、 -i | -include(没试过)
输出为C语言的包含文件形式。 除非xxd从标准输入读入,不然会输出一个完整的静态数组定义(与输入文件同名)
6、 -l len | -len len
输出<len>个字符后停止。
以下输出16个字符后停止
[vagrant@vm-node1:btc]$ echo -n "0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d" | xxd -l 16
00000000: 3063 3238 6663 6133 3836 6337 6132 3237 0c28fca386c7a227
0c28fca386c7a227:这个字符串共16个字符
[vagrant@vm-node1:btc]$ cat r1.txt
abdcfg
ehjki123
1414144152
556625225
225
7839
2030j0f20
限制了内容长度,将数据打印到有限的范围.
[vagrant@vm-node1:btc]$ xxd -l 0x5 r1.txt
00000000: 6162 6463 66 abdcf
7、 -p | -ps | -postscript | -plain
以postscript的连续十六进制转储输出。这也叫做纯十六进制转储。
[vagrant@vm-node1:btc]$ echo -n "0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d" | xxd -p
306332386663613338366337613232373630306232666535306237636165
313165633836643362663166626534373162653839383237653139643732
61613164
8、 -r | -revert
逆向操作:把十六进制转储转换(或者打补丁)成二进制形式。如果不输出到标准输出,xxd并不把输出文件截断,而是直接写到输出文件。用 -r -p 来从一个没有行号没有某种列格式的纯十六进制转储读入。附加的空格 和换行可以出现在任何地方。
对文本似乎没什么用,以下没有任何输出
[vagrant@vm-node1:btc]$ echo -n "0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d" | xxd -r
[vagrant@vm-node1:btc]$
加上了-r -p之后:
[vagrant@vm-node1:btc]$ echo -n "0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d" | xxd -r -p
(`
/
|®쇓¿¾Gª
后来创建了一个r.txt
[vagrant@vm-node1:btc]$ xxd r.txt > 123.txt
[vagrant@vm-node1:btc]$ cat r.txt
ab
dc
fg
e
hj
ki123
141
4144152
5566
25225
[vagrant@vm-node1:btc]$ cat 123.txt
00000000: 6162 0a64 630a 6667 0a65 0a68 6a0a 6b69 ab.dc.fg.e.hj.ki
00000010: 3132 330a 3134 310a 3431 3434 3135 320a 123.141.4144152.
00000020: 3535 3636 0a32 3532 3235 0a 5566.25225.
6162 0a64: 61对应10进制的97,对应ASCII表内的a,0a对应10进制的10,对应ASCII表内的“换行键”,换行符号被替换为"."了,
[vagrant@vm-node1:btc]$ xxd -p r.txt
61620a64630a66670a650a686a0a6b693132330a3134310a343134343135
320a353536360a32353232350a
[vagrant@vm-node1:btc]$ xxd -p r.txt > pr.txt #pr.txt内存储的是纯16进制的转储
[vagrant@vm-node1:btc]$ xxd -r -p pr.txt #用-r -p可以将pr.txt的内容重新变回原来的内容
ab
dc
fg
e
hj
ki123
141
4144152
5566
25225
或者直接如下操作:
[vagrant@vm-node1:btc]$ cat r.txt
ab
dc
fg
e
hj
ki123
141
4144152
5566
25225
[vagrant@vm-node1:btc]$ xxd r.txt | xxd -r #转储后立即可以转回来
ab
dc
fg
e
hj
ki123
141
4144152
5566
25225
9、 -seek offset
用在-r之后: 会在 当前 文件的 偏移量 上 增加 <offset>。
-s [+][-]seek
从infile的绝对或者相对偏移量<seek>开始。+表示相对于标准输入当前的位置(如果不是标准输入就没有意义了)。- 表示从文件末尾(如果和+连用:从标准输入当前位置)向前数一些字符,从那个地方开始。如果没有 -s选项,xxd从当前位置开始。
从第5行生成转储文件?!
有个疑问:感觉是从第6个字节输出的,并不是从第5行输出的
[vagrant@vm-node1:btc]$ cat r1.txt
abdcfg
ehjki123
1414144152
556625225
225
7839
2030j0f20
[vagrant@vm-node1:btc]$ xxd r1.txt
00000000: 6162 6463 6667 0a65 686a 6b69 3132 330a abdcfg.ehjki123.
00000010: 3134 3134 3134 3431 3532 0a35 3536 3632 1414144152.55662
00000020: 3532 3235 0a32 3235 0a37 3833 390a 3230 5225.225.7839.20
00000030: 3330 6a30 6632 300a 30j0f20.
[vagrant@vm-node1:btc]$ xxd -s 5 r1.txt
00000005: 670a 6568 6a6b 6931 3233 0a31 3431 3431 g.ehjki123.14141
00000015: 3434 3135 320a 3535 3636 3235 3232 350a 44152.556625225.
00000025: 3232 350a 3738 3339 0a32 3033 306a 3066 225.7839.2030j0f
00000035: 3230 0a 20.
-u
用大写字母。默认的是小写字母。
[vagrant@vm-node1:btc]$ xxd -u r.txt
00000000: 6162 0A64 630A 6667 0A65 0A68 6A0A 6B69 ab.dc.fg.e.hj.ki
00000010: 3132 330A 3134 310A 3431 3434 3135 320A 123.141.4144152.
00000020: 3535 3636 0A32 3532 3235 0A 5566.25225.
-v | -version
显示版本字符串。
[vagrant@vm-node1:btc]$ xxd -v
xxd 2021-10-22 by Juergen Weigert et al.
例:
将文件内容转换为十六进制:
xxd test.txt
使用xxd跳过第n行,想要从第6行开始生成十六进制转储(0x50表示第6行,0x00表示第1行)
xxd -s 0x50 test.txt
将输出限制为特定长度,从第一行(0x00)打印到第5行(0x40)
xxd -l 0x50 test.txt
将文件内容转换为二进制文件:
xxd -b test.txt
设置列长,默认列长度为16,即16个字符,包括空格,将列长度设置为9:现在我们将列长度设置为“9”:
xxd -c 9 test.txt
纯16进制转储,输出保存在hex文件中,并使用cat命令从hex文件中读取输出:
xxd -p test.txt > hex
cat hex
还原hex文件,将纯十六进制转储的反向输出打印为了ASCII格式:
xxd -r -p hex
分组大小字节,将输出分组为多少个八位字节,默认是2个字节,接下来设为8,即8个字节一组,分为2组:
xxd -g 8 test.txt
我们常用命令:
xxd -p test.class >> java.txt 转为16进制
xxd -r -p java.txt >> test.class 还原文件
1.1.1 WIF、WIF-compressed 编码
下面介绍 WIF、WIF-compressed 具体的编码过程:
1、在 16 进制的私钥前面增加 version 字段,Bitcoin 主网为 0x80
,Bitcoin 测试网为 0xef
; 对于 WIF-compressed 形式,则后面还要增加 0x01
。 这个结果称为 extended key,即:
800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d # for WIF 800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d01 # for WIF-compressed
2、对 extended key 计算 SHA-256:
1ce0723b5128b03fcfd2484368f1bbc89dc7c6eddf56589c2eccb2bacbbcafc9 # for WIF b504a81b66924482a289ce571a614c37b122f40cefb0abba0227e143154c2153 # for WIF-compressed
3、再次计算上面结果的 SHA-256:
507a5b8dfed0fc6fe8801743720cedec06aa5c6fca72b07c49964492fb98a714 # for WIF a62019d20340a1de1b5f254f07f2f6c96ad5165218459ab4f3c8f5a7c0e12183 # for WIF-compressed
4、第二次 SHA256 结果的前 4 个字节作为 checksum:
507a5b8d # for WIF a62019d2 # for WIF-compressed
5、把 checksum 加到 extended key 的后面:
800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d507a5b8d # for WIF 800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d01a62019d2 # for WIF-compressed
6、对上面结果进行 Base58Check 编码,即得到 WIF、WIF-compressed 形式:
5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ # WIF KwdMAjGmerYanjeui5SHS7JkmpZvVipYvB2LJGU1ZxJwYvP98617 # WIF-compressed
注:Base58Check 编码表只有 58 个有效字符,具体来说,就是 10 个数字加上 26 个小写字母加上 26 个大写字母,再减去 0OIl
这 4 个容易产生混淆的字符。
1、原始私钥为: 0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d
未压缩的extend_key='800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d'
压缩extend_key='800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d01'
2、对步骤1的extend_key进行sha256运算
未压缩的:
[vagrant@vm-node1:~]$ echo -n "800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d" | xxd -r -p | sha256sum
8147786c4d15106333bf278d71dadaf1079ef2d2440a4dde37d747ded5403592 -
这个结果跟文章中的不一致,文章中这步结果是:1ce0723b5128b03fcfd2484368f1bbc89dc7c6eddf56589c2eccb2bacbbcafc9
压缩的 :
[vagrant@vm-node1:~]$ echo -n "800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d01" | xxd -r -p | sha256sum
b504a81b66924482a289ce571a614c37b122f40cefb0abba0227e143154c2153 -
3、对步骤2结果进行sha256运算
未压缩的:
[vagrant@vm-node1:~]$ echo -n "8147786c4d15106333bf278d71dadaf1079ef2d2440a4dde37d747ded5403592" | xxd -r -p | sha256sum 507a5b8dfed0fc6fe8801743720cedec06aa5c6fca72b07c49964492fb98a714 -
这步骤的结果跟文章中一致了!
压缩的 :
[vagrant@vm-node1:~]$ echo -n "b504a81b66924482a289ce571a614c37b122f40cefb0abba0227e143154c2153" | xxd -r -p | sha256sum a62019d20340a1de1b5f254f07f2f6c96ad5165218459ab4f3c8f5a7c0e12183 -
4、上个步骤的结果取前4个字节,作为checksum
未压缩的:
[vagrant@vm-node1:~]$ echo -n "507a5b8dfed0fc6fe8801743720cedec06aa5c6fca72b07c49964492fb98a714" | xxd -l 8 | xxd -r
507a5b8d
压缩的 :
[vagrant@vm-node1:~]$ echo -n "a62019d20340a1de1b5f254f07f2f6c96ad5165218459ab4f3c8f5a7c0e12183" | xxd -l 8 | xxd -r
a62019d2
5、把extend_key和checksum拼接起来
未压缩的extend_key='800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d'
压缩extend_key='800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d01'
未压缩的:
800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d507a5b8d
压缩的:
800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d01a62019d2
6、对步骤5结果进行base58运算
未压缩的:
[vagrant@vm-node1:~]$ echo -n "800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d507a5b8d" | xxd -r -p | base58
5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ
压缩的:
[vagrant@vm-node1:~]$ echo -n "800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d01a62019d2" | xxd -r -p| base58
KwdMAjGmerYanjeui5SHS7JkmpZvVipYvB2LJGU1ZxJwYvP98617
cryptotools中提供了私钥和WIF相互转换的工具:
main = {
'hrp': 'bc',
'keyhash': b'\x00',
'scripthash': b'\x05',
'wif': b'\x80',
'extended_prv': {
# https://github.com/spesmilo/electrum-docs/blob/master/xpub_version_bytes.rst
ADDRESS.P2PKH: b'\x04\x88\xad\xe4', # xprv
ADDRESS.P2WPKH: b'\x04\xb2\x43\x0c', # zprv
ADDRESS.P2WSH: b'\x02\xaa\x7a\x99', # Zprv
ADDRESS.P2WPKH_P2SH: b'\x04\x9d\x78\x78', # yprv
ADDRESS.P2WSH_P2SH: b'\x02\x95\xb4\x3f' # Yprv
},
'extended_pub': {
# https://github.com/spesmilo/electrum-docs/blob/master/xpub_version_bytes.rst
ADDRESS.P2PKH: b'\x04\x88\xb2\x1e', # xpub
ADDRESS.P2WPKH: b'\x04\xb2\x47\x46', # zpub
ADDRESS.P2WSH: b'\x02\xaa\x7e\xd3', # Zpub
ADDRESS.P2WPKH_P2SH: b'\x04\x9d\x7c\xb2', # ypub
ADDRESS.P2WSH_P2SH: b'\x02\x95\xb4\x3f' # Ypub
},
'utxo_url': 'https://blockchain.info/unspent?active={address}',
'rawtx_url': 'https://blockchain.info/rawtx/{txid}?format=hex',
'broadcast_url': 'https://blockchain.info/pushtx'
}
1、##将16进制原始私钥转换为WIF
def wif(self, compressed=False) -> str:
from btctools import base58, sha256
from btctools.network import network
extended = network('wif') + self.bytes() + (b'\x01' if compressed else b'')
hashed = sha256(sha256(extended))
checksum = hashed[:4]
return base58.encode(extended + checksum)
#以priv_key = '0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d'为例
network('wif')='0x80'
未压缩:
extended='80'+'0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d' = '800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d'
hashed=sha256(sha256(extended))
[vagrant@vm-node1:~]$ echo -n "800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d" | xxd -r -p | sha256sum | xxd -r -p | sha256sum
507a5b8dfed0fc6fe8801743720cedec06aa5c6fca72b07c49964492fb98a714 -
所以hashed='507a5b8dfed0fc6fe8801743720cedec06aa5c6fca72b07c49964492fb98a714'
checksum = hashed[:4]
[vagrant@vm-node1:~]$ echo -n "507a5b8dfed0fc6fe8801743720cedec06aa5c6fca72b07c49964492fb98a714" | xxd -p -l 8 | xxd -r -p
507a5b8d
所以checksum='507a5b8d'
extended + checksum = '800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d507a5b8d'
base58.encode(extended + checksum):
对extended + checksum做base58运算:
[vagrant@vm-node1:~]$ echo -n "800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d507a5b8d" | xxd -r -p |base58
5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ
压缩时
extended='80'+'0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d'+'01'='800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d01'
hashed=sha256(sha256(extended))
[vagrant@vm-node1:~]$ echo -n "800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d01" | xxd -r -p | sha256sum | xxd -r -p | sha256sum
a62019d20340a1de1b5f254f07f2f6c96ad5165218459ab4f3c8f5a7c0e12183 -
所以hashed='a62019d20340a1de1b5f254f07f2f6c96ad5165218459ab4f3c8f5a7c0e12183'
checksum = hashed[:4]
[vagrant@vm-node1:~]$ echo -n "a62019d20340a1de1b5f254f07f2f6c96ad5165218459ab4f3c8f5a7c0e12183" | xxd -p -l 8 | xxd -r -p
a62019d2
所以checksum='a62019d2'
extended + checksum = '800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d01a62019d2'
base58.encode(extended + checksum):
对extended + checksum做base58运算:
[vagrant@vm-node1:~]$ echo -n "800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d01a62019d2" | xxd -r -p | base58
KwdMAjGmerYanjeui5SHS7JkmpZvVipYvB2LJGU1ZxJwYvP98617
所以私钥0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d对应的压缩WIF:KwdMAjGmerYanjeui5SHS7JkmpZvVipYvB2LJGU1ZxJwYvP98617
非压缩WIF: 5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ
2、#将WIF转换为16进制原始私钥
def from_wif(cls, wif: str) -> 'PrivateKey':
from btctools import base58, sha256
from btctools.network import network
bts = base58.decode(wif)
network_byte, key, checksum = bts[0:1], bts[1:-4], bts[-4:]
assert sha256(sha256(network_byte + key))[:4] == checksum, 'Invalid Checksum'
assert network_byte == network('wif'), 'Invalid Network byte'
if key.endswith(b'\x01'):
key = key[:-1]
compressed = True # TODO
else:
compressed = False # TODO
return cls(key)
#以上面的为例:
#压缩WIF: KwdMAjGmerYanjeui5SHS7JkmpZvVipYvB2LJGU1ZxJwYvP98617
#非压缩WIF: 5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ
#计算原始私钥
bts = base58.decode(wif)
压缩时:
[vagrant@vm-node1:~]$ echo -n "KwdMAjGmerYanjeui5SHS7JkmpZvVipYvB2LJGU1ZxJwYvP98617" | base58 -d |xxd -p
800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d
72aa1d01a62019d2
bts='800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d01a62019d2'
非压缩时:
[vagrant@vm-node1:~]$ echo -n "5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ" | base58 -d |xxd -p
800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d
72aa1d507a5b8d
bts='800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d507a5b8d'
network_byte, key, checksum = bts[0:1], bts[1:-4], bts[-4:]
上个步骤得到bts,此时可以求得network_byte, key, checksum
压缩时:
network_byte='80'
key='0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d01'
checksum='a62019d2'
非压缩时:
network_byte='80'
key='0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d'
checksum='507a5b8d'
assert sha256(sha256(network_byte + key))[:4] == checksum, 'Invalid Checksum'
#这个断言用于校验计算checksum是否一致:
压缩时:
network_byte + key = '800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d01'
checksum='a62019d2'
sha256(sha256(network_byte + key))[:4] :
[vagrant@vm-node1:~]$ echo -n "800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d01" | xxd -r -p | sha256sum | xxd -r -p | sha256sum |xxd -p -l 8 | xxd -r -p
a62019d2
证明checksum是一致的
非压缩时:
network_byte + key = '800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d'
checksum='507a5b8d'
sha256(sha256(network_byte + key))[:4] :
[vagrant@vm-node1:~]$ echo -n "800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d" | xxd -r -p |sha256sum | xxd -r -p | sha256sum |xxd -p -l 8 | xxd -r -p
507a5b8d
证明checksum是一致的
assert network_byte == network('wif'), 'Invalid Network byte'
#这个断言用于校验网络类型 是否一致,显然此时是一致的
最终得到:
压缩时:
key='0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d01'
key=key[:-1]='0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d'
非压缩时:
key='0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d'
所以原始priv_key='0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d'
P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
# Generator
G = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798, 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8
# Order
N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
CURVE = ECDSA.Curve(P, 0, 7, G, N, name='secp256k1')
计算公钥:
def to_public(self) -> 'PublicKey':
point = CURVE.G * self.int() #调用__mul__()
return PublicKey(point)
ECDSA: __init__.py:
def __mul__(self, other: int):
assert isinstance(other, int), 'Multiplication is only defined between a point and an integer'
return self.curve.point_mul(self, other)
class Curve:
def __init__(self, P, a, b, G, N, name):
self.P = P
self.a = a
self.b = b
self.G = Point(*G, self)
self.N = N
self.name = name
#pow(x, y[, z])
函数是计算 x 的 y 次方,如果 z 在存在,则再对结果进行取模,其结果等效于 pow(x,y) %z
#
def point_add(self, p, q):
"""https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Point_addition"""
P = self.P
if p == q:
lam = (3 * p.x * p.x) * pow(2 * p.y % P, P - 2, P)
else:
lam = pow(q.x - p.x, P - 2, P) * (q.y - p.y) % P
rx = lam ** 2 - p.x - q.x
ry = lam * (p.x - rx) - p.y
return Point(rx % P, ry % P, curve=self)
def point_mul(self, p, d):
d = d % self.N
n = p
q = None
exe_time = 0
fmt = format(d, 'b')
rev = reversed(fmt)
for i in rev:
if i == '1':
exe_time = exe_time + 1
if q is None:
q = n
else:
q = self.point_add(q, n)
n = self.point_add(n, n)
return q
key_to_addr_versions = {
ADDRESS.P2PKH: lambda pub: legacy_address(pub, version_byte=network('keyhash')),
# 'P2WPKH': partial(pubkey_to_p2wpkh, version_byte=0x06, witver=0x00), # WAS REPLACED BY BIP 173
ADDRESS.P2WPKH_P2SH: lambda pub: legacy_address(witness_byte(witver=0) + push(hash160(pub.encode(compressed=True))), version_byte=network('scripthash')),
ADDRESS.P2WPKH: partial(pubkey_to_bech32, witver=0x00),
}
script_to_addr_versions = {
ADDRESS.P2SH: lambda script: legacy_address(script, version_byte=network('scripthash')),
# 'P2WSH': partial(script_to_p2wsh, version_byte=0x0A, witver=0x00), # WAS REPLACED BY BIP 173
ADDRESS.P2WSH_P2SH: lambda script: legacy_address(witness_byte(witver=0) + push(sha256(script)), version_byte=network('scripthash')),
ADDRESS.P2WSH: partial(script_to_bech32, witver=0x00),
}