Skip to content

Commit

Permalink
Merge pull request #1094 from jasonandjay/btc
Browse files Browse the repository at this point in the history
docs: ECDSA signature analysis & P2PKH
  • Loading branch information
yingjingyang authored Apr 18, 2024
2 parents 30d9545 + 6dc8697 commit 566b42b
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 0 deletions.
87 changes: 87 additions & 0 deletions BTC/payment/P2PKH.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# 简介

[P2PKH(Pay-to-Public-Key-Hash)](../wallet/address.js#L42)是比特币网络中最常用的一种交易类型,设计用来将比特币支付到某个具体的公钥哈希,而不是直接到公钥本身。这种方式提供了更高的安全性和隐私保护。在P2PKH交易中,比特币被发送到一个地址,这个地址实际上是持有者公钥的哈希版本。这种地址通常以数字"1"开头。地址不仅简洁,而且通过哈希公钥,隐藏了公钥的实际内容,增加了隐私保护层。

# 工作过程

## ScriptPubKey(锁定脚本)

P2PKH脚本模式包含一个公钥哈希,该哈希和以下操作码共同构成锁定脚本

```js
// 模版
OP_DUP OP_HASH160 <Public Key Hash> OP_EQUALVERIFY OP_CHECKSIG

OP_DUP // 复制栈顶的元素
OP_HASH160 // 弹出栈顶元素,计算其SHA-256散列,然后计算RIPEMD-160散列
OP_PUSHBYTES_20 // 推送20字节到栈顶
18e1fad25b2983d5dbb2e2b96e3ce756a69b3bc2 // 公钥哈希
OP_EQUALVERIFY // 比较栈顶元素
OP_CHECKSIG // 验证数字签名

```

## ScriptSig(解锁脚本)

要解锁花费此脚本,上面公钥的所有者需要提供原始公钥以及的有效签名

```js
// 模版
<Singature> <Public Key>

OP_PUSHBYTES_72 // 推送72字节到栈顶
3045022100c233c3a8a510e03ad18b0a24694ef00c78101bfd5ac075b8c1037952ce26e91e02205aa5f8f88f29bb4ad5808ebc12abfd26bd791256f367b04c6d955f01f28a772401 // 签名数据
OP_PUSHBYTES_33 // 推送33字节到栈顶
03480b6822120e9936b43859d84c380583c3d0292409b21453ae962815090f8117 // 压缩公钥
```

## 执行过程

1. 脚本合并,ScriptSig在前ScriptPubKey在后

```js
<Singature> <Public Key> OP_DUP OP_HASH160 <Public Key Hash> OP_EQUALVERIFY OP_CHECKSIG
```

2. 栈内执行

- OP_DUP 从栈里取出公钥,复制并做HASH160推进栈顶
- OP_EQUALVERIFY 对比上一步的计算结果和Public Key Hash
- OP_CHECKSIG 校验数字签名,如果通过锁定脚本合法可以花费对应UTXO,如果不通过交易失败

## 地址生成

P2PKH支付方式从公钥生成地址遵循以下详细步骤:

### 步骤1: 计算公钥哈希

1. **计算公钥的SHA-256哈希值**:首先,对公钥进行SHA-256哈希运算。
2. **计算RIPEMD-160哈希值**:然后,对SHA-256的结果再进行RIPEMD-160哈希运算。这两步哈希运算的结果称为公钥哈希(PKH)。

### 步骤2: 添加版本字节

- **添加版本字节**:在公钥哈希前添加一个版本字节(比特币主网络的P2PKH地址的版本是0x00, 测试网是0x6f)。这有助于钱包软件识别和处理不同类型的地址。

### 步骤3: 计算校验和

1. **双重SHA-256哈希**:对带有版本字节的公钥哈希进行两次SHA-256哈希运算。
2. **取前四个字节**:从双重哈希的结果中取出前四个字节,这部分称为校验和。

### 步骤4: 生成地址

- **组合和Base58编码**:将版本字节和公钥哈希以及校验和组合在一起,整个结构为:
```js
[version byte][public key hash][checksum]
```
然后,对整个字节串进行Base58编码,以生成最终的P2PKH地址。

### 示例

假设有一个公钥,步骤如下:

1. **公钥**`04480b6822120e9936b43859d84c380583c3d0292409b21453ae962815090f8117883c2a3fd7571b12f34491809d48467dae4e2f162aef23de91e4532d0fc1e0c5`
2. **SHA-256哈希**:计算结果。
3. **RIPEMD-160哈希**:计算结果。
4. **添加版本字节**`00` + [RIPEMD-160哈希结果]
5. **计算校验和**:对上述结果执行两次SHA-256,取前四个字节。
6. **Base58编码**:将最终字节串转换为Base58编码:13GZvxKVCw1YadDCBJ7rAuerLiYGXLQBVp
30 changes: 30 additions & 0 deletions BTC/payment/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# BTC地址

在比特币系统中,随着技术的发展和用户需求的变化,钱包现在支持多种不同类型的地址格式,每种格式都对应不同的技术标准和特点。这些地址格式包括传统的P2PKH(以1开头),P2SH(以3开头),以及更加现代的SegWit地址(通常以bc1q或bc1p开头)。进行交易时,我们需要为收款者创建一个交易输出,这个输出指定了收款方的地址和交易的金额。

# BTC地址到交易完成

下面以P2PKH(Pay-to-Public-Key-Hash)为例介绍从比特币收款地址到交易构建的详细步骤:

1. 收款地址的获取和格式
收款方提供一个P2PKH类型的比特币地址,这种地址通常以数字1开始,例如 1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2。这个地址是经过编码的,通常使用Base58Check编码,其中包含了收款方的公钥哈希(public key hash)。

2. Base58Check解码
当付款方接收到一个比特币地址后,他们会使用Base58Check解码来提取地址中的公钥哈希。Base58Check编码不仅帮助减少字符混淆(移除了容易混淆的字符如0(零)、O(大写字母O)、I(大写字母i)和l(小写字母L)),还内嵌了一个错误检测机制。

3. 构建锁定脚本(ScriptPubKey)
解码过程提取出的公钥哈希接着用来构建一个所谓的锁定脚本(ScriptPubKey),这个脚本是交易输出的一部分。对于P2PKH地址,锁定脚本的格式通常如下:

```js
OP_DUP OP_HASH160 <Public Key Hash> OP_EQUALVERIFY OP_CHECKSIG
```

4. 构建交易
付款方将锁定脚本放置在新的交易输出中,并指定转账金额。这个输出现在会被添加到新的交易中。除此之外,交易还需要包含至少一个输入(来自付款方的一个或多个以前的交易输出),这些输入提供了足够的资金来覆盖转账金额和网络矿工费。

5. 签名和广播交易
一旦交易构建完毕,付款方需要用他们的私钥对交易进行签名,证明他们有权使用指定的输入资金。完成签名后,交易将被广播到比特币网络,网络的矿工们开始验证这个交易的合法性,如果合法,最终将其加入到区块链中,完成资金的转移。

# 常见支付方式

- [P2PKH](./P2PKH.md)
36 changes: 36 additions & 0 deletions BTC/signature/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,42 @@ s:是对交易信息、私钥和一个随机数的数学运算的结果。
4. 计算 $u_1 = z \* r^{-1} \mod n$ 和 $u_2 = s \* r^{-1} \mod n$
5. 计算公钥 $Q = (x', y') = u_1 \* G + u_2 \* R $

### 签名分析
#### 签名内容
对于使用ECDSA的比特币交易,签名通常包括两部分:r 和 s,每部分通常是32字节,但也可能因数字的大小而稍有变化。因此,一个典型的ECDSA签名大约是64字节,但当包括签名编码和额外的元数据(如签名哈希类型)时,长度可能会变长。
#### 签名的具体长度
具体到比特币的签名,它通常包括以下几个部分:
- r值:32字节
- s值:32字节
- 签名哈希类型(SIGHASH):通常是1字节
此外,签名还可能包括序列化格式的前缀(如0x30),以及长度描述符,这些都会增加几个字节。

#### 签名案例
```js
// 签名
3045022100c233c3a8a510e03ad18b0a24694ef00c78101bfd5ac075b8c1037952ce26e91e02205aa5f8f88f29bb4ad5808ebc12abfd26bd791256f367b04c6d955f01f28a772401
```

##### DER编码的ECDSA签名结构
一个典型的ECDSA签名的DER编码格式如下:
- 0x30: 表示这是一个序列(SEQUENCE),后面跟随的是整个序列的长度。
- 紧接着的第一个字节(或两个字节,取决于长度)是整个签名结构的长度。
- 0x02: 表示一个整数(INTEGER)的开始,这是签名的第一部分,即r值。
- 紧接着的一个字节(或两个字节)是r值的长度。
- r值本身。
- 另一个0x02,表示第二个整数的开始,这是签名的第二部分,即s值。
- 紧接着的一个字节(或两个字节)是s值的长度。
- s值本身。
##### 分析具体内容
1. 30: 序列开始。
2. 45: 表示序列的长度(十进制的69字节)。
3. 02: r值的整数开始。
4. 21: r值的长度(十进制的33字节,包含前导0,因为r值开始于00,这通常用于表示正数以避免整数被误认为负数)。
5. 00c233c3a8a510e03ad18b0a24694ef00c78101bfd5ac075b8c1037952ce26e91e: r值。
6. 02: s值的整数开始。
7. 20: s值的长度(十进制的32字节)。
8. 5aa5f8f88f29bb4ad5808ebc12abfd26bd791256f367b04c6d955f01f28a7724: s值。
9. 最后的01是签名的SIGHASH类型,表示这是一个标准的所有输出签名。

## Schnorr
比特币的Schnorr签名是一种相对较新的签名算法,它与传统的椭圆曲线数字签名算法(ECDSA)相比,提供了几个关键优势。在2020年的比特币协议升级中,Schnorr签名通过BIP340提案得到了引入。以下是Schnorr签名的详细介绍:
Expand Down

0 comments on commit 566b42b

Please sign in to comment.