主页 > imtokenios下载 > 一窥比特币核心机制的工作原理

一窥比特币核心机制的工作原理

imtokenios下载 2024-01-25 05:14:06

标签: 公钥私钥 bit snooping addressaddresspublic operation

比特币真的很酷。 当然,有人想知道它是否是一项有用的技术,我们目前是否处于加密货币泡沫中,或者它目前面临的治理问题是否会得到解决......但在纯技术层面上,神秘的中本聪创造了一项令人印象深刻的技术.

不幸的是,虽然有许多资源对比特币的工作原理进行了高级解释,但我强烈推荐的此类视频资源之一是 Anders Brownworth 的区块链视觉 101,其中基本信息很少。 在我看来,如果你从 10,000 英尺的高度看,你就会正确地理解这一点。

作为新手,我发现自己渴望了解比特币的运作机制。 幸运的是,由于比特币本质上是去中心化和点对点的,任何人都可以开发符合协议的客户端。 为了更好地了解比特币的工作原理,我决定编写自己的小玩具比特币客户端,能够将交易发布到比特币区块链。

这篇文章介绍了创建最小可行比特币客户端的过程,该客户端可以创建交易并将其提交到比特币对等网络以将其包含在区块链中。 如果您只是阅读原始代码,请随时查看我的 Github 存储库。

地址生成

要成为比特币网络的一部分,必须有一个可以发送和接收资金的地址。 比特币使用公钥加密,地址基本上是从公钥私钥导出的公钥哈希版本。 令人惊讶的是,与大多数公钥加密不同,公钥也是加密存储的,直到资金从地址发送出去——但之后会有更多差异和惊喜。

快速术语说明:在比特币中,客户使用术语钱包来指代地址集合。 协议层没有钱包的概念,只有地址。

比特币对其地址使用椭圆曲线公钥密码术。 在非常高的层次上,椭圆曲线密码术用于从私钥生成公钥,与 RSA 相同下列哪个不是比特币的核心机制,但占用空间更小。 如果您有兴趣学习一些有关其工作原理的数学知识,Cloudflare 的入门书是一个很好的资源。

从一个256位的私钥开始,生成比特币地址的过程如下:

比特币地址生成

在 Python 中,我使用 ecsda 库来完成椭圆曲线加密的繁重工作。 下面的代码片段通过非常容易记住(并且非常不安全)的私钥 0xFEEDB0BDEADBEEF(前面有足够多的零使其长度为 64 个十六进制字符或 256 位)获取公钥。 如果你想在地址中存储任何实际值,你需要一种更安全的方式来生成私钥!

作为一个有趣的测试,我最初使用私钥 0xFACEBEEF 创建了一个地址并向其发送了 0.0005BTC,1 个月后,有人**偷走了我的 0.0005BTC!**我猜人们必须偶尔使用简单的公私钥来搜索地址。 您真的必须使用正确的密钥派生技术!

从 ecdsa 导入 SECP256k1,SigningKey

def get_private_key(hex_string):

return bytes.fromhex(hex_string.zfill(64)) # 将十六进制字符串填充为所需的 64 个字符

def get_public_key(private_key):

# 这将返回所提供的私有地址的串联 x 和 y 坐标

# 前缀 04 用于表示它是未压缩的

返回 (bytes.fromhex("04") + SigningKey.from_string(private_key, curve=SECP256k1).verifying_key.to_string())

private_key = get_private_key("FEEDB0BDEADBEEF")

public_key = get_public_key(private_key)

运行此代码以获取私钥(十六进制)

0000000000000000000000000000000000000000000000000feedb0bdeadbeef

和公钥(十六进制)

04d077e18fd45c031e0d256d75dfa8c3c21c589a861c4c33b99e64cf613113fcff9fc9d90a9d81346bcac64d3c01e6e0ef0828543edad73c0e257b845812cc8d28

公钥前面的 0x04 表示这是一个未压缩的公钥,这意味着 ECDSA x 和 y 坐标只是连接在一起。 因为ECSDA的工作方式,如果知道x值,y值只能取两个值,一偶一奇。 使用此信息,可以使用 x 和 y 的极性来表示公钥。 这将公钥大小从 65 位减少到 33 位,并且据说密钥(以及随后计算的地址)被压缩了。 对于压缩公钥,前置值将是 0x02 或 0x03,具体取决于 y 的极性。 未压缩的公钥最常用于比特币,所以这也是我在这里使用的。

从这里开始,从公钥生成比特币地址,该公钥是一个 sha256 哈希,后跟一个 cookedmd160 哈希。 这种双重哈希提供了额外的安全层,而 ripemd160 哈希提供了 sha256 的 256 位哈希的 160 位哈希,从而减少了地址的长度。 一个有趣的结果是两个不同的公钥可以散列到同一个地址! 然而,对于 2^160 个不同的地址,这不太可能很快发生。

导入哈希库

def get_public_address(public_key):

地址 = hashlib.sha256(public_key).digest()

h = hashlib.new('ripemd160')

H。 更新(地址)

地址 = h. 消化()

退货地址

public_address = get_public_address(public_key)

以上代码生成一个公网地址c8db639c24f6dc026378225e40459ba8a9e54d1a。 这有时称为哈希 160 地址。

如前所述,有趣的一点是私钥到公钥的转换和公钥到公共地址的转换是单向转换。 如果您有地址,则向后查找关联公钥的唯一方法是求解 SHA256 哈希。 这与大多数公钥密码学略有不同,例如公开发布您的公钥并隐藏您的私钥。 在这种情况下,公钥和私钥是隐藏的,地址(哈希公钥)是公开的。

隐藏公钥有充分的理由。 虽然从相应的公钥计算私钥通常是不可行的,但如果生成私钥的方法已被破坏,那么访问公钥可以更容易地推断出私钥。 2013年,臭名昭著的安卓比特币钱包事件。 Android 在随机数生成方面有一个严重的弱点,它为攻击者打开了一个从公钥中找到私钥的途径。 这也是为什么不鼓励在比特币中重复使用地址的原因——签署交易时,您需要透露您的公钥。 如果您在从地址发送交易后不重复使用地址,则无需担心地址的公钥被暴露。

表示比特币地址的标准方式是使用其 Base58Check 编码。 这种编码只是地址的表示(因此可以解码/反转)。 Base58Check 生成格式为 1661HxZpSy5jhcJ2k6av2dxuspa8aafDac 的地址。 Base58Check 编码提供了一个更短的地址来表达,并且还有一个内置的校验和,可以检测错误的地址。 在几乎每个比特币客户端中,您都会看到地址的 Base58Check 编码。 Base58Check 还包括一个版本号,我在下面的代码中将其设置为 0 - 这表示该地址是一个公钥哈希。

# 使用 58 个字母表

BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'

def base58_encode(版本,public_address):

"""

获取 Base58Check 字符串

"""

版本 = 字节。 来自十六进制(版本)

校验和 = hashlib.sha256(hashlib.sha256(version + public_address).digest()).digest()[:4]

有效负载 = 版本 + public_address + 校验和

结果 = int.from_bytes(有效载荷,byteorder =“大”)

打印(结果)

# 计算前导 0

padding = len(payload) - len(payload.lstrip(b'\0'))

编码= []

而结果!= 0:

结果,余数 = divmod(结果,58)

encoded.append(BASE58_ALPHABET[剩余])

返回填充*“1”+“”。加入(编码)[::-1]

bitcoin_address = base58_encode("00", public_address)

毕竟,从我的私钥 feedb0bdeadbeef(前面用零填充)开始,我的比特币地址是 1KK2xni6gmTtdnSGRiuAf94jciFgRjDj7W!。

有了地址,是时候获得一些比特币了! 为了将比特币存入我的地址,我从 btcmarkets 购买了 0.0045 澳元的 BTC(在撰写本文时约为 11 美元)。 使用btcmarket的交易入口,我将其转入上述地址,过程中损失了0.0005BTC的交易手续费。 您可以在交易 95855ba9f46c6936d7b5ee6733c81e715ac92199938ce30ac3e1214b8c2cd8d7 中的区块链上看到此交易。

连接到 p2p 网络

现在我有了一个包含一些比特币的地址,事情变得更有趣了。 如果我想在其他地方发送比特币,我需要连接到比特币节点网络。

前言

考虑到网络的去中心化性质,我第一次了解比特币时遇到的困难之一是网络中的节点如何找到其他节点? 如果没有中央集权,比特币客户端如何知道如何引导并开始与网络其他通信部分进行交互?

事实证明,理想主义体现了实用性,并且在最初的同行发现过程中存在最小的中心化。 新客户找到要连接的节点的主要方式是使用 DNS 查找到由比特币社区成员维护的任意数量的 DNS 种子服务器。

事实证明,DNS 非常适合用于引导客户端的目的,因为 DNS 协议运行在 UDP 上并且非常轻量级,很难用于 DDoS。 IRC 被用作以前的引导方法,但由于易受 DDoS 攻击而被终止。

种子被硬编码到比特币核心的源代码中,但它可能会被核心开发人员更改。

以下 Python 代码连接到 DNS 种子并随机选择要连接的第一个节点。 使用套接字库,代码基本上执行 nslookup 并在针对种子节点 seed.bitcoin.sipa.be 运行查询时返回第一个结果的 ipv4 地址。

进口套接字

# 使用对种子比特币 DNS 服务器的 DNS 请求来查找节点

nodes = socket.getaddrinfo("seed.bitcoin.sipa.be", None)

# 任意选择第一个节点

节点=节点[0][4][0]

运行后返回的地址是208.67.251.126,是我可以连接的友好节点!

向新节点问好

节点之间的比特币连接是通过 TCP 建立的。 当连接到一个节点时,比特币协议的开始握手消息是一个版本消息。 在节点交换版本消息之前,不会接受其他消息。

比特币协议消息在比特币开发者参考中有详细记录。 使用开发人员参考作为指南,可以在 Python 中构建版本消息,如下面的代码片段所示。 大多数数据是相当无趣的,用于建立节点连接的管理数据。 如果您对附件的详细信息感兴趣,请阅读开发人员参考。

版本 = 70014

services = 1 # 不是全节点,不能提供任何数据

时间戳 = int(time.time())

地址接收服务 = 1

addr_recvipaddress = socket.inet_pton(socket.AF_INET6, "::ffff:127.0.0.1") #大端接收节点ip地址

地址接收端口 = 8333

地址_transservices = 1

addr_transipaddress = socket.inet_pton(socket.AF_INET6, "::ffff:127.0.0.1")

地址传输 = 8333

随机数 = 0

user_agentbytes = 0

起始高度 = 329167

继电器 = 0

使用 Python 的结构库,将版本负载数据打包成正确的格式,特别注意数据的字节顺序和字节宽度。 将数据打包成正确的格式很重要,否则接收节点将无法理解它接收到的原始字节。

有效载荷 = 结构。 盒(”

标签:公钥下列哪个不是比特币的核心机制,私钥,比特,窥探,地址,地址,公开,操作