700-Bitcoin基础
0x-wen

1.私钥生成规则

私钥由 24 组数字组成,每 11 个数字为一组,共计 264 个二进制数字(24×11=264)

  • 私钥的组成 = 随机二进制数 + 校验和
  • 私钥有一部分是随机生成的,
  • 最后 8 位叫作校验和(checksum)是以前面的随机部分作为输入通过一个公式计算得到

会随机生成23组二进制数据,每11位一组,最后第24组只会生成3位

23*11+3 = 256 位随机数 + 8位校验和 = 264位【最终私钥的二进制】

在 BIP39 标准下,一个单词需要 11 位数字表达,这里有 264/11 = 24个单词

生成的私钥组合后 就是一个 264位的二进制数据 【缺点是不方便人类记忆】

1.1校验和

随机熵 256位 + 8位校验和 = 264位二进制数 / 11个二进制一组 = 24组数字,对应24个单词【助记词】

随机熵 224位 + 7位校验和 = 231位二进制 / 11个二进制一组 = 21组数字,对应21个单词

随机熵 192位 + 6位校验和 = 198位二进制 / 11个二进制一组 = 18组数字,对应18个单词

随机熵 160位 + 5位校验和 = 165位二进制 / 11个二进制一组 = 15组数字,对应15个单词

随机熵 128位 + 4位校验和 = 132位二进制 / 11个二进制一组 = 12组数字,对应12个单词

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
// 校验随机熵,128, 160 , 192, 224, 256
func validateEntropyBitSize(bitSize int) error {
if (bitSize%32) != 0 || bitSize < 128 || bitSize > 256 {
return ErrEntropyLengthInvalid
}
return nil
}

// 256/8 = 32个 byte
func NewEntropy(bitSize int) ([]byte, error) {
err := validateEntropyBitSize(bitSize)
if err != nil {
return nil, err
}

entropy := make([]byte, bitSize/8)
_, err = rand.Read(entropy)
return entropy, err
}

// 创建助记词
/*
case1: 随机熵256位二进制数据时,校验和为 256/32 = 8
entropyBitLength = 256
checksumBitLength = 256/32
case2: 随机熵128位二进制数据时,校验和为 128/32 = 4
entropyBitLength = 128
checksumBitLength = 128/32
case3: 随机熵224位二进制数据时,校验和为 224/32 = 7
entropyBitLength = 224
checksumBitLength = 224/32
*/
func NewMnemonic(entropy []byte) (string, error) {
// Compute some lengths for convenience.
entropyBitLength := len(entropy) * 8
checksumBitLength := entropyBitLength / 32
sentenceLength := (entropyBitLength + checksumBitLength) / 11

1.2私钥转换

每11个二进制数一组,一共24组。如果都用十进制来表示:可表示的十进制数范围是 “0” 至 “2047”,一组最多可以表示2048个十进制数,[ 00000000000 至 11111111111 ]

转换为24组十进制的数据,每组取值范围都是 0~2047 【还是不利于记忆】

BIP 39 单词表共包含 2048 个单词,按照字母顺序排列。

每个单词都代表 “0” 至 “2047” 之间的某个数字。私钥中的每组十进制数都可以被写成一个单词。

协议制定的,用这个单词就代表用了对应数字。

十进制数 543 对应的是dumb,原因是github上是从1开始,所以行号为544

bips/bip-0039/english.txt at 22660ad3078ee9bd106e64d44662a59a1967c4bd · bitcoin/bips

2.HD钱包

为什么要有一个规定?因为这是不同加密社区正式商定的推导路径。

我可以按照我想要的任何制定标准创建自己的树,并从公钥/私钥创建和签署有效交易。

因为有了协议标准,其他钱包也能重现这种结构,不会导致用户的钱被锁定在完全任意的私钥/公钥树中

image

HD 钱包树由到第一个地址节点的派生路径表示。

例如,以太坊的默认值是 m/44’/60’/0’/0。该路径中的每个数字代表上面树中的某个级别。

1
m / purpose' / coin_type' / account' / chain / address_index

路径 m/44'/60'/0'/0 解构

  • 44 标准协议
  • 60 以太坊 coin_type 解决兼容不同链的问题
  • 0 账户 account 解决不同身份地址管理,比如财务和人事部门 使用不同的Account
  • 0 链 chain 0 是外部地址,对于btc中 1 是内部地址或找零地址

image

【扩展私钥无法签署交易,只能使用子地址中对应的私钥】

注:钱包本身不保存资产,资产是在记录比特币网络账本中的(通常称为保存在链上),钱包实际是一个管理私钥(生成、存储、签名)的工具

钱包为了显示你的比特币 “余额”,钱包软件必须在比特币区块链上查询所有由你的私钥控制的 UTXO,然后将这些 UTXO 的值相加,并显示最终余额。

3.地址生成逻辑

  1. 首先生成一个随机数作为私钥
  2. 然后根据椭圆曲线算法(ECDSA)计算出公钥
  3. 然后在根据哈希运算及校验编码得到比特币地址

公钥及地址是公开的,私钥这是保密的,私钥推导地址的过程也是单向的,无法通过地址反推到公钥及私钥。

image

4.地址类型

4.1 传统的 P2PKH 地址

全称:支付到公钥哈希值(Pay-to-Public-Key-Hash)

地址格式:以 1 开头

字符长度:32 个字符

说明:这种地址类型是比特币最早的地址格式,用户通过公钥哈希来接收比特币。它的安全性依赖于公钥的私钥。

4.2 兼容隔离见证 P2SH-P2WPKH 地址

全称:支付到脚本哈希值(Pay-to-Script-Hash)

地址格式:以 3 开头

字符长度:32 个字符

说明:这种地址类型也称为嵌套隔离见证地址,允许用户在不完全支持隔离见证的环境中使用隔离见证的优势。它通过脚本哈希来接收比特币。

4.3 原生隔离见证 P2WPKH 地址

全称:支付到原生公钥哈希值(Pay-to-Witness-Public-Key-Hash)

地址格式:以 bc1 开头

字符长度:42 个字符

说明:这种地址类型是比特币的原生隔离见证地址,使用 Bech32 编码,提供更高的交易效率和更低的手续费。

4.4 Taproot 地址 P2TR

全称:支付到 Taproot 地址(Pay-to-Taproot)

地址格式:以 bc 开头

字符长度:62 个字符

说明:Taproot 是比特币协议的一个重要升级,旨在提高隐私性和智能合约的灵活性。它允许更复杂的交易结构,同时保持简单的支付方式。

5.UTXO集

所有 UTXO 的集合被称为 UTXO 集。比特币节点会追踪 UTXO 集,确定哪些代币未被花费哪些可以花费。

UTXO 其实是包含

  • 一定数量的比特币(以 “聪(satoshi)” 为单位)
  • 以及花费这些比特币时所需满足的条件 叫作 锁定脚本(locking script)

当我们要使用一个 UTXO 时,就是用私钥对 UTXO 进行解锁(签名),以便使用其中的比特币。

手续费: Input - out = fee

out中: PubKey Script 字段(简称为 “scriptPubKey”)就是我们所说的 “锁定脚本”, 别人的公钥锁定

Input中:Signature Script 字段(简称为 “scriptSig”)也就是所谓的 “解锁脚本”,自己的私钥解锁

5.1 coinbase

Coinbase 交易是一种特殊类型的交易,通过创建新的比特币来奖励找到区块的矿工。

由于创造了新的比特币,coinbase 交易没有输入,但是会产生一个或多个输出。

就像所有正常输出一样,coinbase 交易的输出是新的 UTXO。

6.交易过程

6.1 发起交易及交易签名

  1. 交易输入: 他们指向之前的交易创建的 UTXO,通常钱包会收集当前可用的 UTXO 集合作为交易的输入;
  2. 交易输出:表明多少比特币会锁定到哪些地址,即生成新的 UTXO 。
  3. 签名: 用私钥来解锁交易输入的UTXO。

创建比特币交易是在钱包内完成的,而不是在节点上,因此可以在离线的情况下创建交易,交易创建后,通过比特币节点发送到比特币网络中。

6.2 节点验证交易有效性

无论交易来自哪里,交易就得先被节点的交易池(mempool)接受。交易池就是未确认交易的缓存,以便矿工从中挑选出手续费率最高的交易、打包到区块中。

当然矿工有首先检查交易是否有效,例如检查:

  • 交易输出是否 小于 0 或者大于 2100 万 BTC
  • 交易不是一笔 coinbase 交易 ,因为区块之外无法存在任何 coinbase 交易。
  • 交易的 “重量” 不超过 400000 单位 。这么大体积的交易可能在共识上是有效的,但会占据太多的交易池空间。防止攻击者尝试使用体积非常大但永远不会被挖出的交易来塞爆交易池。
  • 签名验证确定 UTXO 的有效性(UTXO 脚本检查)

所有有效的交易会传播给网络中的所有节点。

6.3 使用工作量证明挖掘新区块

矿工从从交易池中找出最优的一组交易(在一个区块限制下,手续费收益最大)

给这组交易创建merkle树,然后不断的执行暴力哈希运算,以求解出满足一下 Hash 目标值的nonce值

1
SHA256(SHA256(version + prev_hash + merkle_root + ntime + nbits + nonce )) < HASH 目标值

version: block的版本

prev_hash: 上一个block的hash值

merkle_root: 需要写入的交易记录的hash树的值

ntime: 更新时间

nbits: 当前难度

7.隔离见证

隔离见证(SegWit)是一个由多个 BIP(141、142、143、144 和 145)描述的软分叉

其主要用意是优化比特币交易和区块的结构,将交易的签名(也叫 “脚本签名(scriptSig)”、“witness” 或 “解锁脚本”)从交易中移到一个独立的结构中

SegWit解决了什么问题?

SegWit 的首要目标不是节省区块空间,而是修复交易的不定形漏洞。

在 SegWit 启用之前,尚未上链确认的交易的 ID (txid)可能会因为所纳入的脚本或者签名本身的变化而发生变化。

SegWit 将脚本签名(ScriptSig)转移到了交易的一个新部分 “witness”(该部分不用来计算 txid)中,交易的不定形漏洞得以修复,交易 ID 也变成了未确认交易可以依赖的标识符。

这一更改让交易的 ID 变成可以依赖的数据,但将脚本和签名数据移到 Witness 字段中使得开发者必须提出一种新的交易费计量方法,根据 “block weight”[不同类型的数据设计了数据量的乘数] 而非 “block size”[数据量的大小] 计算交易费

在 SegWit 启用之前,区块大小是 100 万字节(约 1 MB)

而在 SegWit 启用之后,区块大小上限变成了 400 万 weight,换算过来就是平均每区块 1.5 ~ 2.0 MB 左右(具体视区块中包含的交易量而定),但是最多可容纳 4 MB 的数据。

这是因为 witness 数据和交易中其它数据的 weight 比是 1:4,区块中可以塞入更多交易,连带会让手续费水平降低。

SegWit 升级见证了广泛的用户群体站出来与贪婪的矿工对抗,通过拒绝非 SegWit 的区块来迫使矿工升级。

它利用了比特币的博弈经济学来降低矿工的动力 — 如果他们的区块会被拒绝,就不能从中收获区块奖励和手续费

由 Hexo 驱动 & 主题 Keep
总字数 42.8k