比特派app最新版下载苹果|比特币挖矿怎么挖python

比特派app最新版下载苹果 2024-03-16 06:28:45

Python 从零开始构建自己的比特币区块链系统 - 知乎

Python 从零开始构建自己的比特币区块链系统 - 知乎首发于Python / Masonite切换模式写文章登录/注册Python 从零开始构建自己的比特币区块链系统Summer摈弃世俗浮躁,追求技术精湛这是一篇社区协同翻译的文章,已完成翻译,更多信息请点击 协同翻译介绍 。你是否会和我一样,对加密数字货币底层的区块链技术非常感兴趣,特别想了解他们的运行机制。但是学习区块链技术并非一帆风顺,我看多了大量的视频教程还有各种课程,最终的感觉就是真正可用的实战课程太少。我喜欢在实践中学习,尤其喜欢一代码为基础去了解整个工作机制。如果你我一样喜欢这种学习方式,当你学完本教程时,你将会知道区块链技术是如何工作的。写在开始之前记住,区块链是一个 不可变的、有序的 被称为块的记录链。它们可以包含交易、文件或任何您喜欢的数据。但重要的是,他们用哈希 一起被链接在一起。如果你不熟悉哈希, 这里是一个解释.该指南的目的是什么? 你可以舒服地阅读和编写基础的Python,因为我们将通过HTTP与区块链进行讨论,所以你也要了解HTTP的工作原理。我需要准备什么? 确定安装了 Python 3.6+ (还有 pip) ,你还需要安装 Flask、 Requests 库:`pip install Flask==0.12.2 requests==2.18.4`对了, 你还需要一个支持HTTP的客户端, 比如 Postman 或者 cURL,其他也可以。源码在哪儿? 可以点击这里Step 1: 创建一个区块链打开你最喜欢的文本编辑器或者IDE, 我个人比较喜欢 PyCharm. 新建一个名为blockchain.py的文件。 我们将只用这一个文件就可以。但是如果你还是不太清楚, 你也可以参考 源码.描述区块链我们要创建一个 Blockchain 类 ,他的构造函数创建了一个初始化的空列表(要存储我们的区块链),并且另一个存储交易。下面是我们这个类的实例:blockchain.pyclass Blockchain(object):

def __init__(self):

self.chain = []

self.current_transactions = []

def new_block(self):

# Creates a new Block and adds it to the chain

pass

def new_transaction(self):

# Adds a new transaction to the list of transactions

pass

@staticmethod

def hash(block):

# Hashes a Block

pass

@property

def last_block(self):

# Returns the last Block in the chain

pass我们的 Blockchain 类负责管理链式数据,它会存储交易并且还有添加新的区块到链式数据的Method。让我们开始扩充更多Method块是什么样的 ?每个块都有一个 索引,一个 时间戳(Unix时间戳),一个事务列表, 一个 校验(稍后详述) 和 前一个块的散列 。下面是一个Block的例子 :blockchain.pyblock = {

'index': 1,

'timestamp': 1506057125.900785,

'transactions': [

{

'sender': "8527147fe1f5426f9dd545de4b27ee00",

'recipient': "a77f5cdfa2934df3954a5c7c7da5df1f",

'amount': 5,

}

],

'proof': 324984774000,

'previous_hash': "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"

}在这一点上,一个 区块链 的概念应该是明显的 - 每个新块都包含在其内的前一个块的 散列 。 这是至关重要的,因为这是 区块链 不可改变的原因:如果攻击者损坏 区块链 中较早的块,则所有后续块将包含不正确的哈希值。这有道理吗? 如果你还没有想通,花点时间仔细思考一下 - 这是区块链背后的核心理念。添加交易到区块我们将需要一个添加交易到区块的方式。我们的 new_transaction()方法的责任就是这个, 并且它非常的简单:blockchain.pyclass Blockchain(object):

...

def new_transaction(self, sender, recipient, amount):

"""

Creates a new transaction to go into the next mined Block

:param sender: Address of the Sender

:param recipient: Address of the Recipient

:param amount: Amount

:return: The index of the Block that will hold this transaction

"""

self.current_transactions.append({

'sender': sender,

'recipient': recipient,

'amount': amount,

})

return self.last_block['index'] + 1new_transaction() 方法添加了交易到列表,它返回了交易将被添加到的区块的索引---讲开采下一个这对稍后对提交交易的用户有用。创建新的区块当我们的 Blockchain 被实例化后,我们需要将 创世 区块(一个没有前导区块的区块)添加进去进去。我们还需要向我们的起源块添加一个 证明,这是挖矿的结果(或工作证明)。 我们稍后会详细讨论挖矿。除了在构造函数中创建 创世 区块外,我们还会补全 new_block() 、 new_transaction() 和 hash() 函数:blockchain.pyimport hashlib

import json

from time import time

class Blockchain(object):

def __init__(self):

self.current_transactions = []

self.chain = []

# 创建创世区块

self.new_block(previous_hash=1, proof=100)

def new_block(self, proof, previous_hash=None):

"""

创建一个新的区块到区块链中

:param proof: 由工作证明算法生成的证明

:param previous_hash: (Optional) 前一个区块的 hash 值

:return: 新区块

"""

block = {

'index': len(self.chain) + 1,

'timestamp': time(),

'transactions': self.current_transactions,

'proof': proof,

'previous_hash': previous_hash or self.hash(self.chain[-1]),

}

# 重置当前交易记录

self.current_transactions = []

self.chain.append(block)

return block

def new_transaction(self, sender, recipient, amount):

"""

创建一笔新的交易到下一个被挖掘的区块中

:param sender: 发送人的地址

:param recipient: 接收人的地址

:param amount: 金额

:return: 持有本次交易的区块索引

"""

self.current_transactions.append({

'sender': sender,

'recipient': recipient,

'amount': amount,

})

return self.last_block['index'] + 1

@property

def last_block(self):

return self.chain[-1]

@staticmethod

def hash(block):

"""

给一个区块生成 SHA-256 值

:param block: Block

:return:

"""

# 我们必须确保这个字典(区块)是经过排序的,否则我们将会得到不一致的散列

block_string = json.dumps(block, sort_keys=True).encode()

return hashlib.sha256(block_string).hexdigest()上面的代码应该是直白的 --- 为了让代码清晰,我添加了一些注释和文档说明。 我们差不多完成了我们的区块链。 但在这个时候你一定很疑惑新的块是怎么被创建、锻造或挖掘的。工作量证明算法使用工作量证明(PoW)算法,来证明是如何在区块链上创建或挖掘新的区块。PoW 的目标是计算出一个符合特定条件的数字,这个数字对于所有人而言必须在计算上非常困难,但易于验证。这是工作证明背后的核心思想。我们将看到一个简单的例子帮助你理解:假设一个整数 x 乘以另一个整数 y 的积的 Hash 值必须以 0 结尾,即 hash(x * y) = ac23dc...0。设 x = 5,求 y ?用 Python 实现:from hashlib import sha256

x = 5

y = 0 # We don't know what y should be yet...

while sha256(f'{x*y}'.encode()).hexdigest()[-1] != "0":

y += 1

print(f'The solution is y = {y}')结果是: y = 21。因为,生成的 Hash 值结尾必须为 0。hash(5 * 21) = 1253e9373e...5e3600155e860在比特币中,工作量证明算法被称为 Hashcash ,它和上面的问题很相似,只不过计算难度非常大。这就是矿工们为了争夺创建区块的权利而争相计算的问题。 通常,计算难度与目标字符串需要满足的特定字符的数量成正比,矿工算出结果后,就会获得一定数量的比特币奖励(通过交易)。验证结果,当然非常容易。实现工作量证明让我们来实现一个相似 PoW 算法。规则类似上面的例子:找到一个数字 P ,使得它与前一个区块的 proof 拼接成的字符串的 Hash 值以 4 个零开头。blockchain.pyimport hashlib

import json

from time import time

from uuid import uuid4

class Blockchain(object):

...

def proof_of_work(self, last_proof):

"""

Simple Proof of Work Algorithm:

- Find a number p' such that hash(pp') contains leading 4 zeroes, where p is the previous p'

- p is the previous proof, and p' is the new proof

:param last_proof:

:return:

"""

proof = 0

while self.valid_proof(last_proof, proof) is False:

proof += 1

return proof

@staticmethod

def valid_proof(last_proof, proof):

"""

Validates the Proof: Does hash(last_proof, proof) contain 4 leading zeroes?

:param last_proof: Previous Proof

:param proof: Current Proof

:return: True if correct, False if not.

"""

guess = f'{last_proof}{proof}'.encode()

guess_hash = hashlib.sha256(guess).hexdigest()

return guess_hash[:4] == "0000"衡量算法复杂度的办法是修改零开头的个数。使用 4 个来用于演示,你会发现多一个零都会大大增加计算出结果所需的时间。现在 Blockchain 类基本已经完成了,接下来使用 HTTP requests 来进行交互。Step 2: Blockchain 作为 API 接口我们将使用 Python Flask 框架,这是一个轻量 Web 应用框架,它方便将网络请求映射到 Python 函数,现在我们来让 Blockchain 运行在基于 Flask web 上。我们将创建三个接口:/transactions/new 创建一个交易并添加到区块/mine 告诉服务器去挖掘新的区块/chain 返回整个区块链创建节点我们的“Flask 服务器”将扮演区块链网络中的一个节点。我们先添加一些框架代码:blockchain.pyimport hashlib

import json

from textwrap import dedent

from time import time

from uuid import uuid4

from flask import Flask

class Blockchain(object):

...

# Instantiate our Node

app = Flask(__name__)

# Generate a globally unique address for this node

node_identifier = str(uuid4()).replace('-', '')

# Instantiate the Blockchain

blockchain = Blockchain()

@app.route('/mine', methods=['GET'])

def mine():

return "We'll mine a new Block"

@app.route('/transactions/new', methods=['POST'])

def new_transaction():

return "We'll add a new transaction"

@app.route('/chain', methods=['GET'])

def full_chain():

response = {

'chain': blockchain.chain,

'length': len(blockchain.chain),

}

return jsonify(response), 200

if __name__ == '__main__':

app.run(host='0.0.0.0', port=5000)简单的说明一下以上代码:第 15 行:实例化节点。阅读更多关于 Flask 内容。第 18 行:为节点创建一个随机的名称。.第 21 行:实例化 Blockchain 类。第 24--26 行:创建 /mine 接口,GET 方式请求。 第 28--30 行:创建 /transactions/new 接口,POST 方式请求,可以给接口发送交易数据。第 32--38 行:创建 /chain 接口,返回整个区块链。第 40--41 行:服务器运行端口 5000 。发送交易发送到节点的交易数据结构如下:{

"sender": "my address",

"recipient": "someone else's address",

"amount": 5

}因为我们已经有了添加交易的方法,所以基于接口来添加交易就很简单了。让我们为添加事务写函数:blockchain.pyimport hashlib

import json

from textwrap import dedent

from time import time

from uuid import uuid4

from flask import Flask, jsonify, request

...

@app.route('/transactions/new', methods=['POST'])

def new_transaction():

values = request.get_json()

# Check that the required fields are in the POST'ed data

required = ['sender', 'recipient', 'amount']

if not all(k in values for k in required):

return 'Missing values', 400

# Create a new Transaction

index = blockchain.new_transaction(values['sender'], values['recipient'], values['amount'])

response = {'message': f'Transaction will be added to Block {index}'}

return jsonify(response), 201

挖矿挖矿正是神奇所在,它很简单,做了一下三件事:计算工作量证明 PoW通过新增一个交易授予矿工(自己)一个币构造新区块并将其添加到链中blockchain.pyimport hashlib

import json

from time import time

from uuid import uuid4

from flask import Flask, jsonify, request

...

@app.route('/mine', methods=['GET'])

def mine():

# We run the proof of work algorithm to get the next proof...

last_block = blockchain.last_block

last_proof = last_block['proof']

proof = blockchain.proof_of_work(last_proof)

# We must receive a reward for finding the proof.

# The sender is "0" to signify that this node has mined a new coin.

blockchain.new_transaction(

sender="0",

recipient=node_identifier,

amount=1,

)

# Forge the new Block by adding it to the chain

previous_hash = blockchain.hash(last_block)

block = blockchain.new_block(proof, previous_hash)

response = {

'message': "New Block Forged",

'index': block['index'],

'transactions': block['transactions'],

'proof': block['proof'],

'previous_hash': block['previous_hash'],

}

return jsonify(response), 200注意交易的接收者是我们自己的服务器节点,我们做的大部分工作都只是围绕 Blockchain 类方法进行交互。到此,我们的区块链就算完成了,我们来实际运行下.Step 3: 运行区块链你可以使用 cURL 或 Postman 去和 API 进行交互启动 Server:$ python blockchain.py

* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)让我们通过请求 http://localhost:5000/mine ( GET )来进行挖矿:用 Postman 发起一个 GET 请求.创建一个交易请求,请求 http://localhost:5000/transactions/new (POST),如图如果不是使用 Postman,则用一下的 cURL 语句也是一样的:$ curl -X POST -H "Content-Type: application/json" -d '{

"sender": "d4ee26eee15148ee92c6cd394edd974e",

"recipient": "someone-other-address",

"amount": 5

}' "http://localhost:5000/transactions/new"在挖了两次矿之后,就有 3 个块了,通过请求 http://localhost:5000/chain 可以得到所有的块信息{

"chain": [

{

"index": 1,

"previous_hash": 1,

"proof": 100,

"timestamp": 1506280650.770839,

"transactions": []

},

{

"index": 2,

"previous_hash": "c099bc...bfb7",

"proof": 35293,

"timestamp": 1506280664.717925,

"transactions": [

{

"amount": 1,

"recipient": "8bbcb347e0634905b0cac7955bae152b",

"sender": "0"

}

]

},

{

"index": 3,

"previous_hash": "eff91a...10f2",

"proof": 35089,

"timestamp": 1506280666.1086972,

"transactions": [

{

"amount": 1,

"recipient": "8bbcb347e0634905b0cac7955bae152b",

"sender": "0"

}

]

}

],

"length": 3

}Step 4: 一致性(共识)我们已经有了一个基本的区块链可以接受交易和挖矿。但是区块链系统应该是分布式的。既然是分布式的,那么我们究竟拿什么保证所有节点有同样的链呢?这就是一致性问题,我们要想在网络上有多个节点,就必须实现一个一致性的算法注册节点在实现一致性算法之前,我们需要找到一种方式让一个节点知道它相邻的节点。每个节点都需要保存一份包含网络中其它节点的记录。因此让我们新增几个接口:/nodes/register 接收 URL 形式的新节点列表./nodes/resolve 执行一致性算法,解决任何冲突,确保节点拥有正确的链.我们修改下 Blockchain 的 init 函数并提供一个注册节点方法:blockchain.py...

from urllib.parse import urlparse

...

class Blockchain(object):

def __init__(self):

...

self.nodes = set()

...

def register_node(self, address):

"""

Add a new node to the list of nodes

:param address: Address of node. Eg. 'http://192.168.0.5:5000'

:return: None

"""

parsed_url = urlparse(address)

self.nodes.add(parsed_url.netloc)我们用 set 来储存节点,这是一种避免重复添加节点的简单方法.实现共识算法就像先前讲的那样,当一个节点与另一个节点有不同的链时,就会产生冲突。 为了解决这个问题,我们将制定最长的有效链条是最权威的规则。换句话说就是:在这个网络里最长的链就是最权威的。 我们将使用这个算法,在网络中的节点之间达成共识。blockchain.py...

import requests

class Blockchain(object)

...

def valid_chain(self, chain):

"""

Determine if a given blockchain is valid

:param chain: A blockchain

:return: True if valid, False if not

"""

last_block = chain[0]

current_index = 1

while current_index < len(chain):

block = chain[current_index]

print(f'{last_block}')

print(f'{block}')

print("\n-----------\n")

# Check that the hash of the block is correct

if block['previous_hash'] != self.hash(last_block):

return False

# Check that the Proof of Work is correct

if not self.valid_proof(last_block['proof'], block['proof']):

return False

last_block = block

current_index += 1

return True

def resolve_conflicts(self):

"""

This is our Consensus Algorithm, it resolves conflicts

by replacing our chain with the longest one in the network.

:return: True if our chain was replaced, False if not

"""

neighbours = self.nodes

new_chain = None

# We're only looking for chains longer than ours

max_length = len(self.chain)

# Grab and verify the chains from all the nodes in our network

for node in neighbours:

response = requests.get(f'http://{node}/chain')

if response.status_code == 200:

length = response.json()['length']

chain = response.json()['chain']

# Check if the length is longer and the chain is valid

if length > max_length and self.valid_chain(chain):

max_length = length

new_chain = chain

# Replace our chain if we discovered a new, valid chain longer than ours

if new_chain:

self.chain = new_chain

return True

return False第一个方法 valid_chain() 负责检查一个链是否有效,方法是遍历每个块并验证散列和证明。resolve_conflicts() 是一个遍历我们所有邻居节点的方法,下载它们的链并使用上面的方法验证它们。 如果找到一个长度大于我们的有效链条,我们就取代我们的链条。我们将两个端点注册到我们的API中,一个用于添加相邻节点,另一个用于解决冲突:blockchain.py@app.route('/nodes/register', methods=['POST'])

def register_nodes():

values = request.get_json()

nodes = values.get('nodes')

if nodes is None:

return "Error: Please supply a valid list of nodes", 400

for node in nodes:

blockchain.register_node(node)

response = {

'message': 'New nodes have been added',

'total_nodes': list(blockchain.nodes),

}

return jsonify(response), 201

@app.route('/nodes/resolve', methods=['GET'])

def consensus():

replaced = blockchain.resolve_conflicts()

if replaced:

response = {

'message': 'Our chain was replaced',

'new_chain': blockchain.chain

}

else:

response = {

'message': 'Our chain is authoritative',

'chain': blockchain.chain

}

return jsonify(response), 200在这一点上,如果你喜欢,你可以使用一台不同的机器,并在你的网络上启动不同的节点。 或者使用同一台机器上的不同端口启动进程。 我在我的机器上,不同的端口上创建了另一个节点,并将其注册到当前节点。 因此,我有两个节点:http://localhost:5000 和 http://localhost:5001。 注册一个新节点:然后我在节点 2 上挖掘了一些新的块,以确保链条更长。 之后,我在节点1上调用 GET /nodes/resolve,其中链由一致性算法取代:这是一个包,去找一些朋友一起,以帮助测试你的区块链。我希望本文能激励你创造更多新东西。我之所以对数字货币入迷,是因为我相信区块链会很快改变我们看待事物的方式,包括经济、政府、档案管理等。更新:我计划在接下来的第2部分中继续讨论区块链交易验证机制,并讨论一些可以让区块链进行生产的方法。 讨论请前往:使用 Python 一步步搭建自己的区块链 编辑于 2018-01-16 09:52Python区块链(Blockchain)比特币 (Bitcoin)​赞同 270​​添加评论​分享​喜欢​收藏​申请转载​文章被以下专栏收录Python / MasonitePython 论坛的精

python程序实现BTC(比特币)挖矿的完整代码 - Python技术站

python程序实现BTC(比特币)挖矿的完整代码 - Python技术站

首页

教程中心

AI绘画

快速入门

本地部署

模型下载

ChatGPT

ChatGPT注册

API申请与充值

自动化办公

爬虫

操作系统

开发工具New

登录

注册

Python技术站首页Python开发python

python程序实现BTC(比特币)挖矿的完整代码

2023年6月3日 上午12:34

python

实现比特币挖矿的完整代码是一项复杂的任务。以下是一些步骤,可帮助您开始编写这种代码,并向您展示一些示例。

1. 了解比特币挖矿的基础知识

在编写比特币挖矿代码之前,您需要了解比特币挖矿的基础知识。比特币是一种基于区块链技术的加密货币。它的设计目的是通过参与挖矿来保障比特币交易的安全性和稳定性。比特币挖矿需要高性能计算机来解决复杂的数学难题,以获得比特币。

2. 选择编程语言和库

要编写比特币挖矿代码,您需要选择一种适合此类程序编写的编程语言和库。Python是一种流行的编程语言,适用于编写比特币挖矿代码。以下是一些常用的Python库,可用于编写比特币挖矿代码:

Pybitcoin:一个Python库,可用于处理比特币交易和网络协议。

Pycoin:一个Python库,可用于处理比特币协议。

Python-bitcoinlib:一个Python库,可用于处理比特币网络协议和交易。

3. 实现比特币挖矿算法

实现比特币挖矿算法是编写比特币挖矿代码的关键步骤。比特币挖矿算法主要是基于SHA-256算法实现的。以下是一个基于Python的SHA-256算法实现示例:

import hashlib

def SHA256(text):

return hashlib.sha256(text.encode("ascii")).hexdigest()

def mine(block_number, transactions, previous_hash):

prefix = "00000"

nonce = 1

while True:

text = str(block_number) + transactions + previous_hash + str(nonce)

hash = SHA256(text)

if hash.startswith(prefix):

print(f"Bitcoin mined with nonce value: {nonce}")

return hash

nonce += 1

此代码实现了一个基本的比特币挖矿算法。使用SHA-256哈希函数生成的随机哈希值与前缀“00000”进行比较。如果哈希以“00000”开头,则挖出了一枚新的比特币。nonce是一个随机数,用于生成不同的哈希值。

4. 实现比特币交易

在编写比特币挖矿代码时,您需要实现一个简单的比特币交易系统。以下是一个基于Python的比特币交易系统示例:

class Transaction:

def __init__(self, sender, receiver, amount):

self.sender = sender

self.receiver = receiver

self.amount = amount

def __str__(self):

return f"{self.sender} sent {self.amount} BTC to {self.receiver}"

此代码实现了一个简单的比特币交易系统,用于发送和接收比特币。交易包括发送者、接收者和发送的数量。

5. 编写客户端和服务器

要实现比特币挖矿,您需要编写一个客户端和一个服务器。客户端从比特币网络接收交易信息,然后将其转发给服务器。服务器将处理这些交易,并运行比特币挖矿算法。以下是一个基于Python的比特币客户端和服务器示例:

import socket

import json

class Client:

def send(self, data):

host = "localhost"

port = 8000

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

s.connect((host, port))

s.send(json.dumps(data).encode("ascii"))

response = s.recv(1024).decode("ascii")

s.close()

return json.loads(response)

class Server:

def __init__(self):

self.transactions = []

def add_transaction(self, transaction):

self.transactions.append(transaction)

def get_transactions(self):

return self.transactions

def mine(self):

last_block_number = 0

last_hash = "INITIAL HASH"

transactions = self.get_transactions()

new_hash = mine(last_block_number, transactions, last_hash)

return new_hash

s = Server()

c = Client()

t1 = Transaction("Tommy", "Jerry", 10)

s.add_transaction(t1)

print(c.send(s.get_transactions())) # [{'sender': 'Tommy', 'receiver': 'Jerry', 'amount': 10}]

print(c.send(s.mine())) # Bitcoin mined with nonce value: 239095, 00000d26dfa...

此代码实现了一个基本的比特币客户端和服务器。客户端从比特币网络中接收交易信息,并将其转发给服务器。服务器将处理这些交易,并运行比特币挖矿算法。此示例使用了之前实现的交易系统和比特币挖矿算法。

以上是较为简单的比特币挖矿Python代码示例,仅作为参考。在实际开发中,需要应用更加复杂的算法、实现更加稳定的客户端与服务器程序等。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:python程序实现BTC(比特币)挖矿的完整代码 - Python技术站

python

赞 (4)

0 0

打赏

微信扫一扫

支付宝扫一扫

生成海报

python 阿里云oss实现直传签名与回调验证的示例方法

上一篇

2023年6月3日

利用Python编写简易的录制屏幕小工具

下一篇

2023年6月3日

相关文章

python字符串操作详析

Python字符串操作详析 Python是一种解释性语言,具有强大的字符串处理能力。在Python中,对字符串进行操作非常方便,开发者可以使用内置的函数和方法来满足日常需要。本文将详细讲解Python中字符串的各种操作,包括字符串的基本操作,格式化字符串,字符串的搜索和替换,字符串的切片和拼接等。 字符串的基本操作 在Python中,字符串是一种不可变的数据…

python

2023年6月5日

000

使用python-cv2实现视频的分解与合成的示例代码

这里是使用 Python 的 OpenCV 库(cv2)实现视频的分解与合成的完整攻略。 准备工作 在开始之前,需要先安装 OpenCV 库才能运行这项任务。可以通过 pip install 命令安装: pip install opencv-python 视频的读取 首先,让我们看一个从视频中读取每一帧并显示的示例: import cv2 # 加载视频源 v…

python

2023年5月19日

000

Python中的if判断语句中包含or问题

当我们使用Python的 if 条件分支语句时,有时候需要在一个条件判断中包含多个条件,这时就可以使用 or 关键字来连接多个条件。使用 or 时,只需要满足其中一个条件为真(即非零、非空、非 None 等)即可执行相应的代码块。本文将详细讲解如何在 Python 中使用 or 关键字进行条件判断。 基础语法 Python 中使用 or 关键字来连接多个判断…

python

2023年6月3日

000

python正则表达式(re模块)的使用详解

Python正则表达式(re模块)的使用详解 在Python中,正则表达式是一种强大的文本处理工具,可以用于匹配、查找、替换和割字符串。Python的模块提供了一系列的函数和方法,用于处理正则表达式。本文将为您详细讲解Python正则表达式模块)的使用方法,包括正则表达的语法、re模块的常用函数和方法、以及两个示例说明。 正表达式的语法 在正则表达中,使用[…

python

2023年5月14日

000

Python基础学习列表+元组+字典+集合

Python基础学习之列表 1. 列表的定义 列表(List)是Python中最基本的数据结构,为有序的元素集合,每个元素都可以通过下标来访问。可以把Python列表看作一个数组,它可以存储任意类型的对象,包括数字、字符串、元组以及其他列表等。列表用一对方括号“[]”来表示,中间的元素用英文逗号分隔。 2. 基本操作 2.1 列表元素的访问 可以通过下标来访…

python

2023年5月13日

000

基于Python正则表达式提取搜索结果中的站点地址

基于Python正则表达式提取搜索结果中的站点地址 在进行网络爬虫或搜索引擎优化时,经常需要从搜索结果中提取站点地址。本文将为您详细讲解基于Python正则表达式提取搜索结果的站点地址的完整攻略,包括正则表达式的语法、re模块的常方法和两个示例说明。 正则达式的语法 在正则达式中,使用[]表示字符集,^表示取反,-表示范围,+表示匹配一个或多个字符,*表示匹…

python

2023年5月14日

000

bluepy 一款python封装的BLE利器简单介绍

Bluepy 一款 python 封装的 BLE 利器简单介绍 什么是 Bluepy Bluepy 是一款 Python 封装的 Bluetooth Low Energy (BLE) 工具。它允许你通过简单的 Python 代码来进行 BLE 设备的扫描、连接、探索和数据通信等操作。 bluepy 的安装 在 Linux 环境下安装 bluepy 非常简单,…

python

2023年6月3日

000

Python3 常用数据标准化方法详解

下面是详细讲解“Python3常用数据标准化方法详解”的完整攻略。 1. 什么是数据标准化 数据标准化指将数据转换特定范围内的标准值的过程。标准化可以使不同单位或不同量级的数据具有可比性,从而更易进行数据分析和处理。在数据分析和机学习中,数据标准化是一个重要的预处理步骤,可以提高模型准确性稳定性。 2. 常用的数据标准化方法 以下是常用的数据标准化方法: 2…

python

2023年5月14日

000

热门标签

python

人工智能

node js

Pandas

django

Nginx

爬虫

Docker

NumPy

卷积神经网络

目标检测

机器学习

rabbitmq

循环神经网络

pip

Unity

wcf

apache

热门文章

Python查询列表元素的5种常用方法

Python 如何自定义模块(详解版)

Python 关闭文件(close)函数使用方法

Python 写入文件数据(write)函数使用方法

Python小数类型(float)详解

详解Python中复数类型的创建、比较与运算!

Python 反转序列(reversed函数)使用方法

Python 将字符串转换为代码的函数(eval和exec)详解

Python 读取文件(read)函数使用方法

Python 空值None用法详解

关于我们

隐私政策

© 2022-2024 Python技术站 保留所有权利

辽公网安备21010502000733号 辽ICP备18014290号

合作推广

分享本页

返回顶部

用15行Python代码进行比特币挖矿,Python比特币教程

用15行Python代码进行比特币挖矿,Python比特币教程

Toolify

产品

最新推出

最新上架的AI,每日更新

最多保存

获得最多收藏的AI工具

流量最高

网站流量最高的AI工具

AI 浏览器插件

按浏览器插件分类的AI工具

AI Apps

按App分类的AI工具

分类

排行榜

AI月榜

依据月份和月访问量划分的AI网站榜单

AI分类榜

依据分类和月访问量划分的AI网站榜单

AI地区榜

依据地区和月访问量划分的AI网站榜单

AI渠道榜

依据来源和月访问量划分的AI网站榜单

AI收入榜

依据支付排名和实际流量的AI高收入榜

高收入AI

GPTs

提交

推广

收藏

登录

zh

English

简体中文

繁體中文

한국어

日本語

Português

Español

Deutsch

Français

Tiếng Việt

Toolify

产品

最新推出

最新上架的AI,每日更新

最多保存

获得最多收藏的AI工具

流量最高

网站流量最高的AI工具

AI 浏览器插件

按浏览器插件分类的AI工具

AI Apps

按App分类的AI工具

排行榜

AI月榜

依据月份和月访问量划分的AI网站榜单

AI分类榜

依据分类和月访问量划分的AI网站榜单

AI地区榜

依据地区和月访问量划分的AI网站榜单

AI渠道榜

依据来源和月访问量划分的AI网站榜单

AI收入榜

依据支付排名和实际流量的AI高收入榜

分类

高收入AI

GPTs

提交

Promote

收藏

zh

English

简体中文

繁體中文

한국어

日本語

Português

Español

Deutsch

Français

Tiếng Việt

登录

Optimyzee

< 5K

92.22%

10

Revolutionary AI-powered ad management tool for Google Ads.

AI Ad Creative Assistant

AI Ad Generator

AI Advertising Assistant

Aicupid

35.9K

14.14%

12

NSFW AI Chat with no filter

AI Chatbot

AI Girlfriend

AI Character

NSFW

AI Photo & Image Generator

soona

146.1K

63.74%

2

Professional + UGC content made easy.

AI Reviews Assistant

AI Design Generator

AI Graphic Design

Design Assistant

E-commerce Assistant

AI Photo & Image Generator

Photo & Image Editor

AI Photography

AI Repurpose Assistant

AI Art Generator

AI Colorize

Kin AI

20.5K

32.1%

61

Your personal AI for a private life.

AI Chatbot

Life Assistant

Writing Assistants

Tripo AI

32.8K

29.78%

7

Generate 3D assets from a single image instantly

AI Product Description Generator

Text to 3D

AI 3D Model Generator

Image to 3D Model

AI Smash or Pass

13.2K

17.87%

6

Create and customize AI influencers with ease.

AI Character

AI Photo & Image Generator

AI Social Media Assistant

AI Avatar Generator

AI Profile Picture Generator

AI Instagram Assistant

用15行Python代码进行比特币挖矿,Python比特币教程

Find AI Tools

No difficulty

No complicated process

Find ai tools

Home

/

AI News CN

/

用15行Python代码进行比特币挖矿,Python比特币教程

Cesar Hernando Prieto

Updated on Jan 19,2024

facebook

Twitter

linkedin

pinterest

reddit

用15行Python代码进行比特币挖矿,Python比特币教程目录

1. 什么是比特币挖矿?

1.1 比特币和区块链的理论背后

1.2 比特币挖矿的基本原理

1.3 区块链的结构和组成要素

1.4 密码学和哈希函数的基本概念

1.5 挖矿的难度和奖励机制

2. 如何进行比特币挖矿?

2.1 编写比特币挖矿代码的基本思路

2.2 生成交易记录和区块链

2.3 寻找满足难度要求的哈希值

2.4 挖矿的计算复杂性和时间成本

2.5 比特币挖矿的硬件需求与收益评估

3. 比特币挖矿的局限性和挑战

3.1 挖矿难度的不断增加

3.2 能源消耗和环境影响

3.3 挖矿算力的集中化问题

3.4 硬件和技术的迅速过时

4. 比特币挖矿的未来展望

4.1 区块链技术的广泛应用前景

4.2 挖矿的可持续性和创新方向

4.3 加密货币的发展趋势和风险

4.4 挖矿参与者的选择和策略

5. 结论

第一章 什么是比特币挖矿?

1.1 比特币和区块链的理论背后

比特币是一种基于区块链技术的加密货币,而区块链是一种去中心化、安全且不可篡改的分布式账本系统。比特币的挖矿过程是指通过计算力参与区块链网络,验证交易并创建新的区块。

1.2 比特币挖矿的基本原理

比特币挖矿的原理涉及到密码学和哈希函数的应用。哈希函数是一种将任意长度的输入转化为固定长度输出的函数。挖矿过程通过不断尝试不同的输入值,即随机数(nonce),并计算其哈希值,以寻找满足难度要求的哈希值。

1.3 区块链的结构和组成要素

区块链是由一个个区块构成,每个区块包含了一定数量的交易记录和其他元数据。区块之间通过将上一个区块的哈希值作为自身的前一个哈希值,形成了一条链式结构。

1.4 密码学和哈希函数的基本概念

密码学是比特币挖矿的核心技术之一,其中哈希函数扮演了重要角色。哈希函数的输出结果是一个固定长度的哈希值,用于表示输入数据的摘要信息。

1.5 挖矿的难度和奖励机制

比特币挖矿的难度由网络共识算法决定,即首位若干位哈希值必须为0。挖矿过程是竞争性的,首个找到符合要求的哈希值的矿工将获得一定数量的比特币奖励。

第二章 如何进行比特币挖矿?

2.1 编写比特币挖矿代码的基本思路

比特币挖矿代码的基本思路是通过计算不同的随机数(nonce),将交易记录和其他数据组合成一个字符串,然后使用哈希函数计算其哈希值,再检查是否满足难度要求。

2.2 生成交易记录和区块链

在进行比特币挖矿之前,需要生成一些虚拟的交易记录和区块链数据,以便进行挖矿操作。这些数据将模拟真实的交易和区块链结构。

2.3 寻找满足难度要求的哈希值

比特币挖矿的关键是寻找满足难度要求的哈希值。通过尝试不同的随机数(nonce),不断计算哈希值,并与困难目标进行比较,直到找到符合要求的哈希值。

2.4 挖矿的计算复杂性和时间成本

比特币挖矿的计算复杂性和时间成本取决于挖矿难度和计算能力。随着挖矿难度的增加和计算能力的限制,挖矿所需的时间和资源成本也相应增加。

2.5 比特币挖矿的硬件需求与收益评估

比特币挖矿对硬件的要求很高,普通计算机很难完成挖矿任务。挖矿的收益取决于比特币的价格和挖矿所需的时间和资源成本。

第三章 比特币挖矿的局限性和挑战

3.1 挖矿难度的不断增加

比特币挖矿的难度随着时间的推移不断增加,导致挖矿变得更加困难和耗时。这对于一般用户和小型矿工来说是一项巨大的挑战。

3.2 能源消耗和环境影响

比特币挖矿需要大量的计算能力和能源消耗,导致对环境产生了负面影响。能源消耗和环境污染是比特币挖矿面临的重要问题。

3.3 挖矿算力的集中化问题

比特币挖矿的算力集中在少数大型矿场和矿池中,导致了算力的不平衡和中心化问题。这可能对比特币网络的安全性和去中心化性质产生影响。

3.4 硬件和技术的迅速过时

比特币挖矿涉及到大量的硬件和技术投资,但这些投资可能很快过时。随着技术的不断发展和更新,旧的挖矿设备可能很快变得无效。

第四章 比特币挖矿的未来展望

4.1 区块链技术的广泛应用前景

区块链技术不仅仅用于比特币挖矿,在金融、物流、供应链和其他领域都有广泛的应用前景。随着技术的发展和成熟,区块链将发挥更大的作用。

4.2 挖矿的可持续性和创新方向

比特币挖矿需要寻求可持续性的发展模式,并通过创新和改进来减少对能源的消耗和环境的影响。新的挖矿算法和技术可能进一步推动挖矿的发展。

4.3 加密货币的发展趋势和风险

加密货币的发展趋势和风险是比特币挖矿的重要考量因素。随着加密货币市场的不断变化和监管政策的调整,进行挖矿需要谨慎判断和评估。

4.4 挖矿参与者的选择和策略

作为挖矿参与者,选择适合自己的挖矿策略和平台是关键。合理的风险管理和投资决策将决定挖矿的成功与否。

第五章 结论

比特币挖矿是一个复杂而有挑战性的过程,需要高度的计算能力和专业硬件设备。挖矿的成功与否取决于多个因素,包括挖矿难度、计算成本和市场风险等。在未来,比特币挖矿将继续发展,但也需要面对逐渐增长的挑战和变化。

零代码创建情感分析模型!

Autonomic:连接现代交通系统的云平台

Most people like

Chaindesk AI

19.5K

7.75%

18

Create custom AI chatbots with Chaindesk for streamlined customer support.

AI Chatbot

Large Language Models (LLMs)

No-Code&Low-Code

AI Product Description Generator

AI Reply Assistant

AI Response Generator

AD

Immersive Fox

8.4K

9.35%

1

Generate videos in minutes with realistic face & voice clones.

AI Analytics Assistant

AI CRM Assistant

AI Email Marketing

AI Lead Generation

Text to Video

AI Personalized Video Generator

AI Email Assistant

AI Email Generator

AI Response Generator

Sales Assistant

AD

Are you spending too much time looking for ai tools?

App rating

4.9

AI Tools

100k+

Trusted Users

5000+

Email address

Find ai tools

WHY YOU SHOULD CHOOSE TOOLIFY

TOOLIFY is the best ai tool source.

Browse More Content

Related Articles

AI Art Controversy: Lensa App's Impact

AI Art Controversy: Lensa App's ImpactTable of Contents

Introduction

What is AI Art?

The Rise of AI

Trey Mykel

Oct 20,2023

Revolutionize After Effects with AI-Generated Scripts

Revolutionize After Effects with AI-Generated ScriptsTable of Contents

Introduction

The Power of AI

Jake In Motion

Oct 20,2023

Creating Flappy Bird with AI: Chat GPT Adventure

Creating Flappy Bird with AI: Chat GPT AdventureTable of Contents

Introduction

The AI Revolution in

Asmongold TV

Oct 21,2023

Refresh Articles

Toolify: The Best AI Websites & AI Tools Directory

AI Tools list

AI Websites list

GPTs Store

Pick Your AI tools

Toolify,最佳的AI工具导航站

Product

最新推出

最多保存

流量最高

AI 浏览器插件

AI Apps

分类

AI月榜

AI分类榜

AI地区榜

AI渠道榜

AI收入榜

GPTs

提交

推广

收藏

Resourse

博客

AI News CN

Author

Browse by Alphabet

A

B

C

D

E

F

G

H

I

J

K

L

M

N

O

P

Q

R

S

T

U

V

W

X

Y

Z

Other

Top 1000 AI Tools Directory

03/14

03/13

03/12

03/11

03/10

03/09

03/08

03/07

03/06

Read more

告别Midjourney,体验免费Leonardo AI

M Journey 6 vs. Dolly 3:谁是提示一致性之王?

免费神经网络生成标志 | 轻松创作高质量图像

免费的全新AI网站生成文本到图像,创造收入的机会!

Midjourney和Dall-E (ChatGPT)哪个更好的AI图像生成器?

打造热门视频的秘诀和技巧

如何在Adobe Stock中销售AI视频?

用MIDJOURNEY创建个性化图像 | 9分钟教程

开放AI反击纽约时报:诉讼毫无根据!

Chat GPT让DALLE 3彻底击败Mid Journey

人工智能无人机将取代飞行员?点击观看与Flock合作的视频!

商业飞行员的未来:挑战与自动化影响

AI无法取代(优秀的!)财务规划师的原因

AI动画:改变动画未来的机遇与挑战

AI是否夺取销售业务?AI是否会让销售人员消失?

压缩视频不失去质量:使用WinX Video AI!

Rent the Runway:如何将客户置于核心位置

人工智能是否会取代金融顾问?

2024年最佳职业选择:AI还是网络安全?

寻车与重建:机车克拉丽丝的故事

关于

Privacy Policy

联系我们

business@toolify.ai

English

简体中文

繁體中文

한국어

日本語

Português

Español

Deutsch

Français

Tiếng Việt

Copyright ©2024 toolify

Top

用Python从头开始编写比特币 - 知乎

用Python从头开始编写比特币 - 知乎首发于区块链从零开始切换模式写文章登录/注册用Python从头开始编写比特币鼓励师​​南京邮电大学 软件工程硕士翻译自 A from-scratch tour of Bitcoin in Python (karpathy.github.io)这篇文章需要有一点密码学基础、btc相关知识的了解,当你认真把它看完一定有所收获。我觉得区块链很吸引人,因为它将开源软件开发延伸到了开源+状态。这似乎是计算模式中真正/令人兴奋的创新;我们不仅可以分享代码,还可以分享一台正在运行的计算机,而且任何地方的任何人都可以以开放和无许可的方式使用它。这场革命的种子可以说是从比特币开始的,所以我开始好奇地钻研它的一些细节,以获得对它如何工作的直观理解。本着 "我不能创造的东西我不理解 "的精神,还有什么比从头开始实施它更好的方法呢?我们将用纯Python语言创建、数字签名和广播一个比特币交易,从零开始,并且没有任何依赖性。在这个过程中,我们会学到很多关于比特币如何代表价值的知识。让我们开始吧。(btw 如果这篇文章的视觉格式让你不爽,请看jupyter笔记本的版本,它的内容是一样的)。第一步:生成一个加密身份首先,我们要生成一个全新的加密身份,这只是一个私人、公共密钥对。比特币使用椭圆曲线加密法,而不是像RSA那样更常见的东西来保证交易的安全。我不打算在这里对ECC做全面的介绍,因为其他人已经做得很好了,例如我发现Andrea Corbellini的系列博客文章是一个特殊资源。在这里,我们只是要写代码,但要理解它在数学上的工作原理,你需要通过这个系列。好的,所以比特币使用secp256k1曲线。作为这个领域的新手,我发现这部分很吸引人--有整个不同的曲线库,你可以从中选择不同的优点/缺点和属性。NIST发布了关于使用哪些曲线的建议,但人们更愿意使用其他曲线(如secp256k1),因为它们不太可能有后门。总之,椭圆曲线是一个相当低维的数学对象,只需要3个整数来定义。from __future__ import annotations # PEP 563: Postponed Evaluation of Annotations

from dataclasses import dataclass # https://docs.python.org/3/library/dataclasses.html I like these a lot

@dataclass

class Curve:

"""

Elliptic Curve over the field of integers modulo a prime.

Points on the curve satisfy y^2 = x^3 + a*x + b (mod p).

"""

p: int # the prime modulus of the finite field

a: int

b: int

# secp256k1 uses a = 0, b = 7, so we're dealing with the curve y^2 = x^3 + 7 (mod p)

bitcoin_curve = Curve(

p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F,

a = 0x0000000000000000000000000000000000000000000000000000000000000000, # a = 0

b = 0x0000000000000000000000000000000000000000000000000000000000000007, # b = 7

)除了实际的曲线之外,我们还定义了一个生成器点,这只是曲线周期上的某个固定的 "起点",用来启动围绕曲线的 "随机漫步"。生成器是一个公开的、公认的常数。@dataclass

class Point:

""" An integer point (x,y) on a Curve """

curve: Curve

x: int

y: int

G = Point(

bitcoin_curve,

x = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,

y = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8,

)

# we can verify that the generator point is indeed on the curve, i.e. y^2 = x^3 + 7 (mod p)

print("Generator IS on the curve: ", (G.y**2 - G.x**3 - 7) % bitcoin_curve.p == 0)

# some other totally random point will of course not be on the curve, _MOST_ likely

import random

random.seed(1337)

x = random.randrange(0, bitcoin_curve.p)

y = random.randrange(0, bitcoin_curve.p)

print("Totally random point is not: ", (y**2 - x**3 - 7) % bitcoin_curve.p == 0)结果:Generator IS on the curve: True

Totally random point is not: False最后,生成点G的顺序是已知的,它实际上是我们正在处理的(x,y)整数图元的 "集合的大小",即围绕曲线的循环。我喜欢将这些信息组织到一个数据结构中,我称之为生成器。@dataclass

class Generator:

"""

A generator over a curve: an initial point and the (pre-computed) order

"""

G: Point # a generator point on the curve

n: int # the order of the generating point, so 0*G = n*G = INF

bitcoin_gen = Generator(

G = G,

# the order of G is known and can be mathematically derived

n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141,

)请注意,到目前为止,我们还没有真正做任何事情,只是定义了一些数据结构,并用与比特币中使用的椭圆曲线有关的公知常数来填充它们。这一点即将改变,因为我们已经准备好生成我们的私钥。私钥(或称 "秘钥",我以后称之为 "秘钥")只是一个满足1 <= key < n(记得n是G的阶数)的随机整数。# secret_key = random.randrange(1, bitcoin_gen.n) # this is how you _would_ do it

secret_key = int.from_bytes(b'Andrej is cool :P', 'big') # this is how I will do it for reproducibility

assert 1 <= secret_key < bitcoin_gen.n

print(secret_key)

22265090479312778178772228083027296664144这是我们的秘钥--它是一个相当不起眼的整数,但任何知道它的人都可以控制你在比特币区块链上拥有的所有资金,与它相关联。在比特币最简单、最普通的虚构使用案例中,它是控制你账户的单一 "密码"。当然,在极不可能的情况下,一些其他的Andrej像我上面那样手动生成了他们的秘钥,与这个秘钥相关的钱包很可能有一个零比特币的余额 :) 。如果不是这样,我们就真的很幸运了。我们现在要生成公钥,这就是事情开始变得有趣的地方。公钥是曲线上的一个点,这个点是将生成器的点与自身的秘密钥匙次数相加的结果。即我们有:public_key = G + G + G + (secret key times) + G = secret_key * G。注意,这里的'+'(添加)和'*'(次数)符号都非常特别,而且略显混乱。秘钥是一个整数,但生成点G是一个(x,y)元组,是曲线上的一个点,结果是一个(x,y)元组的公钥,也是曲线上的一个点。这就是我们必须实际定义椭圆曲线上的加法运算。它有一个非常具体的定义和几何解释(见Andrea的上述帖子),但实际实现起来相对简单。INF = Point(None, None, None) # special point at "infinity", kind of like a zero

def extended_euclidean_algorithm(a, b):

"""

Returns (gcd, x, y) s.t. a * x + b * y == gcd

This function implements the extended Euclidean

algorithm and runs in O(log b) in the worst case,

taken from Wikipedia.

"""

old_r, r = a, b

old_s, s = 1, 0

old_t, t = 0, 1

while r != 0:

quotient = old_r // r

old_r, r = r, old_r - quotient * r

old_s, s = s, old_s - quotient * s

old_t, t = t, old_t - quotient * t

return old_r, old_s, old_t

def inv(n, p):

""" returns modular multiplicate inverse m s.t. (n * m) % p == 1 """

gcd, x, y = extended_euclidean_algorithm(n, p) # pylint: disable=unused-variable

return x % p

def elliptic_curve_addition(self, other: Point) -> Point:

# handle special case of P + 0 = 0 + P = 0

if self == INF:

return other

if other == INF:

return self

# handle special case of P + (-P) = 0

if self.x == other.x and self.y != other.y:

return INF

# compute the "slope"

if self.x == other.x: # (self.y = other.y is guaranteed too per above check)

m = (3 * self.x**2 + self.curve.a) * inv(2 * self.y, self.curve.p)

else:

m = (self.y - other.y) * inv(self.x - other.x, self.curve.p)

# compute the new point

rx = (m**2 - self.x - other.x) % self.curve.p

ry = (-(m*(rx - self.x) + self.y)) % self.curve.p

return Point(self.curve, rx, ry)

Point.__add__ = elliptic_curve_addition # monkey patch addition into the Point class我承认这看起来有点吓人,理解和重新推导上述内容花了我半天的时间。大部分的复杂性来自于所有的数学运算都是用模块运算完成的。因此,即使是像除法'/'这样的简单运算,也突然需要像模块化乘法逆向入侵这样的算法。但需要注意的是,一切都只是在图元(x,y)上的一堆加法/乘法,中间到处都有一些模数p。让我们通过生成一些微不足道的(私有、公有)密钥对来试一试。# if our secret key was the integer 1, then our public key would just be G:

sk = 1

pk = G

print(f" secret key: {sk}\n public key: {(pk.x, pk.y)}")

print("Verify the public key is on the curve: ", (pk.y**2 - pk.x**3 - 7) % bitcoin_curve.p == 0)

# if it was 2, the public key is G + G:

sk = 2

pk = G + G

print(f" secret key: {sk}\n public key: {(pk.x, pk.y)}")

print("Verify the public key is on the curve: ", (pk.y**2 - pk.x**3 - 7) % bitcoin_curve.p == 0)

# etc.:

sk = 3

pk = G + G + G

print(f" secret key: {sk}\n public key: {(pk.x, pk.y)}")

print("Verify the public key is on the curve: ", (pk.y**2 - pk.x**3 - 7) % bitcoin_curve.p == 0)

secret key: 1

public key: (55066263022277343669578718895168534326250603453777594175500187360389116729240, 32670510020758816978083085130507043184471273380659243275938904335757337482424)

Verify the public key is on the curve: True

secret key: 2

public key: (89565891926547004231252920425935692360644145829622209833684329913297188986597, 12158399299693830322967808612713398636155367887041628176798871954788371653930)

Verify the public key is on the curve: True

secret key: 3

public key: (112711660439710606056748659173929673102114977341539408544630613555209775888121, 25583027980570883691656905877401976406448868254816295069919888960541586679410)

Verify the public key is on the curve: True好了,我们有了上面的一些密钥对,但是我们想要与上面的随机生成的秘钥相关的公钥。如果只使用上面的代码,我们必须把G加到自己身上很多次,因为秘密密钥是一个大的整数。所以结果会是正确的,但运行速度会很慢。相反,让我们实现 "双倍加法 "的算法,以大大加快重复加法的速度。同样,关于它为什么有效,请看上面的帖子,但它就在这里。def double_and_add(self, k: int) -> Point:

assert isinstance(k, int) and k >= 0

result = INF

append = self

while k:

if k & 1:

result += append

append += append

k >>= 1

return result

# monkey patch double and add into the Point class for convenience

Point.__rmul__ = double_and_add

# "verify" correctness

print(G == 1*G)

print(G + G == 2*G)

print(G + G + G == 3*G)

True

True

True

# efficiently calculate our actual public key!

public_key = secret_key * G

print(f"x: {public_key.x}\ny: {public_key.y}")

print("Verify the public key is on the curve: ", (public_key.y**2 - public_key.x**3 - 7) % bitcoin_curve.p == 0)

x: 83998262154709529558614902604110599582969848537757180553516367057821848015989

y: 37676469766173670826348691885774454391218658108212372128812329274086400588247

Verify the public key is on the curve: True有了私钥/公钥对,我们现在已经生成了我们的加密货币身份。现在是时候推导出相关的比特币钱包地址了。钱包地址不仅仅是公钥本身,但它可以确定地从公钥中推导出来,并有一些额外的好处(如一个嵌入式校验)。在我们生成地址之前,我们需要定义一些哈希函数。比特币使用无处不在的SHA-256和RIPEMD-160。我们可以直接使用Python的hashlib中的实现,但这应该是一个零依赖性的实现,所以import hashlib是一种欺骗行为。因此,首先,这里是我按照NIST FIPS PUB 180-4文件(相对可读)用纯Python写的SHA256实现。def gen_sha256_with_variable_scope_protector_to_not_pollute_global_namespace():

"""

SHA256 implementation.

Follows the FIPS PUB 180-4 description for calculating SHA-256 hash function

https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf

Noone in their right mind should use this for any serious reason. This was written

purely for educational purposes.

"""

import math

from itertools import count, islice

# -----------------------------------------------------------------------------

# SHA-256 Functions, defined in Section 4

def rotr(x, n, size=32):

return (x >> n) | (x << size - n) & (2**size - 1)

def shr(x, n):

return x >> n

def sig0(x):

return rotr(x, 7) ^ rotr(x, 18) ^ shr(x, 3)

def sig1(x):

return rotr(x, 17) ^ rotr(x, 19) ^ shr(x, 10)

def capsig0(x):

return rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22)

def capsig1(x):

return rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25)

def ch(x, y, z):

return (x & y)^ (~x & z)

def maj(x, y, z):

return (x & y) ^ (x & z) ^ (y & z)

def b2i(b):

return int.from_bytes(b, 'big')

def i2b(i):

return i.to_bytes(4, 'big')

# -----------------------------------------------------------------------------

# SHA-256 Constants

def is_prime(n):

return not any(f for f in range(2,int(math.sqrt(n))+1) if n%f == 0)

def first_n_primes(n):

return islice(filter(is_prime, count(start=2)), n)

def frac_bin(f, n=32):

""" return the first n bits of fractional part of float f """

f -= math.floor(f) # get only the fractional part

f *= 2**n # shift left

f = int(f) # truncate the rest of the fractional content

return f

def genK():

"""

Follows Section 4.2.2 to generate K

The first 32 bits of the fractional parts of the cube roots of the first

64 prime numbers:

428a2f98 71374491 b5c0fbcf e9b5dba5 3956c25b 59f111f1 923f82a4 ab1c5ed5

d807aa98 12835b01 243185be 550c7dc3 72be5d74 80deb1fe 9bdc06a7 c19bf174

e49b69c1 efbe4786 0fc19dc6 240ca1cc 2de92c6f 4a7484aa 5cb0a9dc 76f988da

983e5152 a831c66d b00327c8 bf597fc7 c6e00bf3 d5a79147 06ca6351 14292967

27b70a85 2e1b2138 4d2c6dfc 53380d13 650a7354 766a0abb 81c2c92e 92722c85

a2bfe8a1 a81a664b c24b8b70 c76c51a3 d192e819 d6990624 f40e3585 106aa070

19a4c116 1e376c08 2748774c 34b0bcb5 391c0cb3 4ed8aa4a 5b9cca4f 682e6ff3

748f82ee 78a5636f 84c87814 8cc70208 90befffa a4506ceb bef9a3f7 c67178f2

"""

return [frac_bin(p ** (1/3.0)) for p in first_n_primes(64)]

def genH():

"""

Follows Section 5.3.3 to generate the initial hash value H^0

The first 32 bits of the fractional parts of the square roots of

the first 8 prime numbers.

6a09e667 bb67ae85 3c6ef372 a54ff53a 9b05688c 510e527f 1f83d9ab 5be0cd19

"""

return [frac_bin(p ** (1/2.0)) for p in first_n_primes(8)]

# -----------------------------------------------------------------------------

def pad(b):

""" Follows Section 5.1: Padding the message """

b = bytearray(b) # convert to a mutable equivalent

l = len(b) * 8 # note: len returns number of bytes not bits

# append but "1" to the end of the message

b.append(0b10000000) # appending 10000000 in binary (=128 in decimal)

# follow by k zero bits, where k is the smallest non-negative solution to

# l + 1 + k = 448 mod 512

# i.e. pad with zeros until we reach 448 (mod 512)

while (len(b)*8) % 512 != 448:

b.append(0x00)

# the last 64-bit block is the length l of the original message

# expressed in binary (big endian)

b.extend(l.to_bytes(8, 'big'))

return b

def sha256(b: bytes) -> bytes:

# Section 4.2

K = genK()

# Section 5: Preprocessing

# Section 5.1: Pad the message

b = pad(b)

# Section 5.2: Separate the message into blocks of 512 bits (64 bytes)

blocks = [b[i:i+64] for i in range(0, len(b), 64)]

# for each message block M^1 ... M^N

H = genH() # Section 5.3

# Section 6

for M in blocks: # each block is a 64-entry array of 8-bit bytes

# 1. Prepare the message schedule, a 64-entry array of 32-bit words

W = []

for t in range(64):

if t <= 15:

# the first 16 words are just a copy of the block

W.append(bytes(M[t*4:t*4+4]))

else:

term1 = sig1(b2i(W[t-2]))

term2 = b2i(W[t-7])

term3 = sig0(b2i(W[t-15]))

term4 = b2i(W[t-16])

total = (term1 + term2 + term3 + term4) % 2**32

W.append(i2b(total))

# 2. Initialize the 8 working variables a,b,c,d,e,f,g,h with prev hash value

a, b, c, d, e, f, g, h = H

# 3.

for t in range(64):

T1 = (h + capsig1(e) + ch(e, f, g) + K[t] + b2i(W[t])) % 2**32

T2 = (capsig0(a) + maj(a, b, c)) % 2**32

h = g

g = f

f = e

e = (d + T1) % 2**32

d = c

c = b

b = a

a = (T1 + T2) % 2**32

# 4. Compute the i-th intermediate hash value H^i

delta = [a, b, c, d, e, f, g, h]

H = [(i1 + i2) % 2**32 for i1, i2 in zip(H, delta)]

return b''.join(i2b(i) for i in H)

return sha256

sha256 = gen_sha256_with_variable_scope_protector_to_not_pollute_global_namespace()

print("verify empty hash:", sha256(b'').hex()) # should be e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

print(sha256(b'here is a random bytes message, cool right?').hex())

print("number of bytes in a sha256 digest: ", len(sha256(b'')))

erify empty hash: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

69b9779edaa573a509999cbae415d3408c30544bad09727a1d64eff353c95b89

number of bytes in a sha256 digest: 32好吧,我之所以想从头开始实现它并把它粘贴在这里,是因为我想让你注意到,里面没有什么太可怕的事情发生。SHA256需要对一些字节信息进行哈希,它首先对信息进行填充,然后将其分解成几块,并将这些几块传给一个可以最好地描述为花哨的 "比特混合器",在第3节中定义,它包含一些比特移动和二进制操作的协调,坦率地说,我不知道这种方式,但这导致了SHA256所提供的美丽特性。特别是,它为任何可变大小的原始信息创建了一个固定大小、随机外观的短摘要,而且加扰是不可反转的,而且从计算上来说,构建一个不同的信息来哈希任何特定的摘要是不可能的。比特币到处使用SHA256来创建哈希值,当然它也是比特币工作证明的核心元素,其目标是修改交易块,直到整个交易块哈希到一个足够低的数字(当摘要的字节被解释为一个数字时)。由于SHA256的良好特性,这只能通过蛮力搜索来完成。因此,所有为高效挖矿而设计的ASIC都只是对上述代码进行了令人难以置信的优化的近似金属的实现。总之,在我们生成地址之前,我们还需要RIPEMD160哈希函数,我在网上找到了这个函数,并进行了缩短和清理。def gen_ripemd160_with_variable_scope_protector_to_not_pollute_global_namespace():

import sys

import struct

# -----------------------------------------------------------------------------

# public interface

def ripemd160(b: bytes) -> bytes:

""" simple wrapper for a simpler API to this hash function, just bytes to bytes """

ctx = RMDContext()

RMD160Update(ctx, b, len(b))

digest = RMD160Final(ctx)

return digest

# -----------------------------------------------------------------------------

class RMDContext:

def __init__(self):

self.state = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0] # uint32

self.count = 0 # uint64

self.buffer = [0]*64 # uchar

def RMD160Update(ctx, inp, inplen):

have = int((ctx.count // 8) % 64)

inplen = int(inplen)

need = 64 - have

ctx.count += 8 * inplen

off = 0

if inplen >= need:

if have:

for i in range(need):

ctx.buffer[have+i] = inp[i]

RMD160Transform(ctx.state, ctx.buffer)

off = need

have = 0

while off + 64 <= inplen:

RMD160Transform(ctx.state, inp[off:])

off += 64

if off < inplen:

for i in range(inplen - off):

ctx.buffer[have+i] = inp[off+i]

def RMD160Final(ctx):

size = struct.pack("

padlen = 64 - ((ctx.count // 8) % 64)

if padlen < 1 + 8:

padlen += 64

RMD160Update(ctx, PADDING, padlen-8)

RMD160Update(ctx, size, 8)

return struct.pack("<5L", *ctx.state)

# -----------------------------------------------------------------------------

K0 = 0x00000000

K1 = 0x5A827999

K2 = 0x6ED9EBA1

K3 = 0x8F1BBCDC

K4 = 0xA953FD4E

KK0 = 0x50A28BE6

KK1 = 0x5C4DD124

KK2 = 0x6D703EF3

KK3 = 0x7A6D76E9

KK4 = 0x00000000

PADDING = [0x80] + [0]*63

def ROL(n, x):

return ((x << n) & 0xffffffff) | (x >> (32 - n))

def F0(x, y, z):

return x ^ y ^ z

def F1(x, y, z):

return (x & y) | (((~x) % 0x100000000) & z)

def F2(x, y, z):

return (x | ((~y) % 0x100000000)) ^ z

def F3(x, y, z):

return (x & z) | (((~z) % 0x100000000) & y)

def F4(x, y, z):

return x ^ (y | ((~z) % 0x100000000))

def R(a, b, c, d, e, Fj, Kj, sj, rj, X):

a = ROL(sj, (a + Fj(b, c, d) + X[rj] + Kj) % 0x100000000) + e

c = ROL(10, c)

return a % 0x100000000, c

def RMD160Transform(state, block): #uint32 state[5], uchar block[64]

x = [0]*16

assert sys.byteorder == 'little', "Only little endian is supported atm for RIPEMD160"

x = struct.unpack('<16L', bytes(block[0:64]))

a = state[0]

b = state[1]

c = state[2]

d = state[3]

e = state[4]

#/* Round 1 */

a, c = R(a, b, c, d, e, F0, K0, 11, 0, x)

e, b = R(e, a, b, c, d, F0, K0, 14, 1, x)

d, a = R(d, e, a, b, c, F0, K0, 15, 2, x)

c, e = R(c, d, e, a, b, F0, K0, 12, 3, x)

b, d = R(b, c, d, e, a, F0, K0, 5, 4, x)

a, c = R(a, b, c, d, e, F0, K0, 8, 5, x)

e, b = R(e, a, b, c, d, F0, K0, 7, 6, x)

d, a = R(d, e, a, b, c, F0, K0, 9, 7, x)

c, e = R(c, d, e, a, b, F0, K0, 11, 8, x)

b, d = R(b, c, d, e, a, F0, K0, 13, 9, x)

a, c = R(a, b, c, d, e, F0, K0, 14, 10, x)

e, b = R(e, a, b, c, d, F0, K0, 15, 11, x)

d, a = R(d, e, a, b, c, F0, K0, 6, 12, x)

c, e = R(c, d, e, a, b, F0, K0, 7, 13, x)

b, d = R(b, c, d, e, a, F0, K0, 9, 14, x)

a, c = R(a, b, c, d, e, F0, K0, 8, 15, x) #/* #15 */

#/* Round 2 */

e, b = R(e, a, b, c, d, F1, K1, 7, 7, x)

d, a = R(d, e, a, b, c, F1, K1, 6, 4, x)

c, e = R(c, d, e, a, b, F1, K1, 8, 13, x)

b, d = R(b, c, d, e, a, F1, K1, 13, 1, x)

a, c = R(a, b, c, d, e, F1, K1, 11, 10, x)

e, b = R(e, a, b, c, d, F1, K1, 9, 6, x)

d, a = R(d, e, a, b, c, F1, K1, 7, 15, x)

c, e = R(c, d, e, a, b, F1, K1, 15, 3, x)

b, d = R(b, c, d, e, a, F1, K1, 7, 12, x)

a, c = R(a, b, c, d, e, F1, K1, 12, 0, x)

e, b = R(e, a, b, c, d, F1, K1, 15, 9, x)

d, a = R(d, e, a, b, c, F1, K1, 9, 5, x)

c, e = R(c, d, e, a, b, F1, K1, 11, 2, x)

b, d = R(b, c, d, e, a, F1, K1, 7, 14, x)

a, c = R(a, b, c, d, e, F1, K1, 13, 11, x)

e, b = R(e, a, b, c, d, F1, K1, 12, 8, x) #/* #31 */

#/* Round 3 */

d, a = R(d, e, a, b, c, F2, K2, 11, 3, x)

c, e = R(c, d, e, a, b, F2, K2, 13, 10, x)

b, d = R(b, c, d, e, a, F2, K2, 6, 14, x)

a, c = R(a, b, c, d, e, F2, K2, 7, 4, x)

e, b = R(e, a, b, c, d, F2, K2, 14, 9, x)

d, a = R(d, e, a, b, c, F2, K2, 9, 15, x)

c, e = R(c, d, e, a, b, F2, K2, 13, 8, x)

b, d = R(b, c, d, e, a, F2, K2, 15, 1, x)

a, c = R(a, b, c, d, e, F2, K2, 14, 2, x)

e, b = R(e, a, b, c, d, F2, K2, 8, 7, x)

d, a = R(d, e, a, b, c, F2, K2, 13, 0, x)

c, e = R(c, d, e, a, b, F2, K2, 6, 6, x)

b, d = R(b, c, d, e, a, F2, K2, 5, 13, x)

a, c = R(a, b, c, d, e, F2, K2, 12, 11, x)

e, b = R(e, a, b, c, d, F2, K2, 7, 5, x)

d, a = R(d, e, a, b, c, F2, K2, 5, 12, x) #/* #47 */

#/* Round 4 */

c, e = R(c, d, e, a, b, F3, K3, 11, 1, x)

b, d = R(b, c, d, e, a, F3, K3, 12, 9, x)

a, c = R(a, b, c, d, e, F3, K3, 14, 11, x)

e, b = R(e, a, b, c, d, F3, K3, 15, 10, x)

d, a = R(d, e, a, b, c, F3, K3, 14, 0, x)

c, e = R(c, d, e, a, b, F3, K3, 15, 8, x)

b, d = R(b, c, d, e, a, F3, K3, 9, 12, x)

a, c = R(a, b, c, d, e, F3, K3, 8, 4, x)

e, b = R(e, a, b, c, d, F3, K3, 9, 13, x)

d, a = R(d, e, a, b, c, F3, K3, 14, 3, x)

c, e = R(c, d, e, a, b, F3, K3, 5, 7, x)

b, d = R(b, c, d, e, a, F3, K3, 6, 15, x)

a, c = R(a, b, c, d, e, F3, K3, 8, 14, x)

e, b = R(e, a, b, c, d, F3, K3, 6, 5, x)

d, a = R(d, e, a, b, c, F3, K3, 5, 6, x)

c, e = R(c, d, e, a, b, F3, K3, 12, 2, x) #/* #63 */

#/* Round 5 */

b, d = R(b, c, d, e, a, F4, K4, 9, 4, x)

a, c = R(a, b, c, d, e, F4, K4, 15, 0, x)

e, b = R(e, a, b, c, d, F4, K4, 5, 5, x)

d, a = R(d, e, a, b, c, F4, K4, 11, 9, x)

c, e = R(c, d, e, a, b, F4, K4, 6, 7, x)

b, d = R(b, c, d, e, a, F4, K4, 8, 12, x)

a, c = R(a, b, c, d, e, F4, K4, 13, 2, x)

e, b = R(e, a, b, c, d, F4, K4, 12, 10, x)

d, a = R(d, e, a, b, c, F4, K4, 5, 14, x)

c, e = R(c, d, e, a, b, F4, K4, 12, 1, x)

b, d = R(b, c, d, e, a, F4, K4, 13, 3, x)

a, c = R(a, b, c, d, e, F4, K4, 14, 8, x)

e, b = R(e, a, b, c, d, F4, K4, 11, 11, x)

d, a = R(d, e, a, b, c, F4, K4, 8, 6, x)

c, e = R(c, d, e, a, b, F4, K4, 5, 15, x)

b, d = R(b, c, d, e, a, F4, K4, 6, 13, x) #/* #79 */

aa = a

bb = b

cc = c

dd = d

ee = e

a = state[0]

b = state[1]

c = state[2]

d = state[3]

e = state[4]

#/* Parallel round 1 */

a, c = R(a, b, c, d, e, F4, KK0, 8, 5, x)

e, b = R(e, a, b, c, d, F4, KK0, 9, 14, x)

d, a = R(d, e, a, b, c, F4, KK0, 9, 7, x)

c, e = R(c, d, e, a, b, F4, KK0, 11, 0, x)

b, d = R(b, c, d, e, a, F4, KK0, 13, 9, x)

a, c = R(a, b, c, d, e, F4, KK0, 15, 2, x)

e, b = R(e, a, b, c, d, F4, KK0, 15, 11, x)

d, a = R(d, e, a, b, c, F4, KK0, 5, 4, x)

c, e = R(c, d, e, a, b, F4, KK0, 7, 13, x)

b, d = R(b, c, d, e, a, F4, KK0, 7, 6, x)

a, c = R(a, b, c, d, e, F4, KK0, 8, 15, x)

e, b = R(e, a, b, c, d, F4, KK0, 11, 8, x)

d, a = R(d, e, a, b, c, F4, KK0, 14, 1, x)

c, e = R(c, d, e, a, b, F4, KK0, 14, 10, x)

b, d = R(b, c, d, e, a, F4, KK0, 12, 3, x)

a, c = R(a, b, c, d, e, F4, KK0, 6, 12, x) #/* #15 */

#/* Parallel round 2 */

e, b = R(e, a, b, c, d, F3, KK1, 9, 6, x)

d, a = R(d, e, a, b, c, F3, KK1, 13, 11, x)

c, e = R(c, d, e, a, b, F3, KK1, 15, 3, x)

b, d = R(b, c, d, e, a, F3, KK1, 7, 7, x)

a, c = R(a, b, c, d, e, F3, KK1, 12, 0, x)

e, b = R(e, a, b, c, d, F3, KK1, 8, 13, x)

d, a = R(d, e, a, b, c, F3, KK1, 9, 5, x)

c, e = R(c, d, e, a, b, F3, KK1, 11, 10, x)

b, d = R(b, c, d, e, a, F3, KK1, 7, 14, x)

a, c = R(a, b, c, d, e, F3, KK1, 7, 15, x)

e, b = R(e, a, b, c, d, F3, KK1, 12, 8, x)

d, a = R(d, e, a, b, c, F3, KK1, 7, 12, x)

c, e = R(c, d, e, a, b, F3, KK1, 6, 4, x)

b, d = R(b, c, d, e, a, F3, KK1, 15, 9, x)

a, c = R(a, b, c, d, e, F3, KK1, 13, 1, x)

e, b = R(e, a, b, c, d, F3, KK1, 11, 2, x) #/* #31 */

#/* Parallel round 3 */

d, a = R(d, e, a, b, c, F2, KK2, 9, 15, x)

c, e = R(c, d, e, a, b, F2, KK2, 7, 5, x)

b, d = R(b, c, d, e, a, F2, KK2, 15, 1, x)

a, c = R(a, b, c, d, e, F2, KK2, 11, 3, x)

e, b = R(e, a, b, c, d, F2, KK2, 8, 7, x)

d, a = R(d, e, a, b, c, F2, KK2, 6, 14, x)

c, e = R(c, d, e, a, b, F2, KK2, 6, 6, x)

b, d = R(b, c, d, e, a, F2, KK2, 14, 9, x)

a, c = R(a, b, c, d, e, F2, KK2, 12, 11, x)

e, b = R(e, a, b, c, d, F2, KK2, 13, 8, x)

d, a = R(d, e, a, b, c, F2, KK2, 5, 12, x)

c, e = R(c, d, e, a, b, F2, KK2, 14, 2, x)

b, d = R(b, c, d, e, a, F2, KK2, 13, 10, x)

a, c = R(a, b, c, d, e, F2, KK2, 13, 0, x)

e, b = R(e, a, b, c, d, F2, KK2, 7, 4, x)

d, a = R(d, e, a, b, c, F2, KK2, 5, 13, x) #/* #47 */

#/* Parallel round 4 */

c, e = R(c, d, e, a, b, F1, KK3, 15, 8, x)

b, d = R(b, c, d, e, a, F1, KK3, 5, 6, x)

a, c = R(a, b, c, d, e, F1, KK3, 8, 4, x)

e, b = R(e, a, b, c, d, F1, KK3, 11, 1, x)

d, a = R(d, e, a, b, c, F1, KK3, 14, 3, x)

c, e = R(c, d, e, a, b, F1, KK3, 14, 11, x)

b, d = R(b, c, d, e, a, F1, KK3, 6, 15, x)

a, c = R(a, b, c, d, e, F1, KK3, 14, 0, x)

e, b = R(e, a, b, c, d, F1, KK3, 6, 5, x)

d, a = R(d, e, a, b, c, F1, KK3, 9, 12, x)

c, e = R(c, d, e, a, b, F1, KK3, 12, 2, x)

b, d = R(b, c, d, e, a, F1, KK3, 9, 13, x)

a, c = R(a, b, c, d, e, F1, KK3, 12, 9, x)

e, b = R(e, a, b, c, d, F1, KK3, 5, 7, x)

d, a = R(d, e, a, b, c, F1, KK3, 15, 10, x)

c, e = R(c, d, e, a, b, F1, KK3, 8, 14, x) #/* #63 */

#/* Parallel round 5 */

b, d = R(b, c, d, e, a, F0, KK4, 8, 12, x)

a, c = R(a, b, c, d, e, F0, KK4, 5, 15, x)

e, b = R(e, a, b, c, d, F0, KK4, 12, 10, x)

d, a = R(d, e, a, b, c, F0, KK4, 9, 4, x)

c, e = R(c, d, e, a, b, F0, KK4, 12, 1, x)

b, d = R(b, c, d, e, a, F0, KK4, 5, 5, x)

a, c = R(a, b, c, d, e, F0, KK4, 14, 8, x)

e, b = R(e, a, b, c, d, F0, KK4, 6, 7, x)

d, a = R(d, e, a, b, c, F0, KK4, 8, 6, x)

c, e = R(c, d, e, a, b, F0, KK4, 13, 2, x)

b, d = R(b, c, d, e, a, F0, KK4, 6, 13, x)

a, c = R(a, b, c, d, e, F0, KK4, 5, 14, x)

e, b = R(e, a, b, c, d, F0, KK4, 15, 0, x)

d, a = R(d, e, a, b, c, F0, KK4, 13, 3, x)

c, e = R(c, d, e, a, b, F0, KK4, 11, 9, x)

b, d = R(b, c, d, e, a, F0, KK4, 11, 11, x) #/* #79 */

t = (state[1] + cc + d) % 0x100000000

state[1] = (state[2] + dd + e) % 0x100000000

state[2] = (state[3] + ee + a) % 0x100000000

state[3] = (state[4] + aa + b) % 0x100000000

state[4] = (state[0] + bb + c) % 0x100000000

state[0] = t % 0x100000000

return ripemd160

ripemd160 = gen_ripemd160_with_variable_scope_protector_to_not_pollute_global_namespace()

print(ripemd160(b'hello this is a test').hex())

print("number of bytes in a RIPEMD-160 digest: ", len(ripemd160(b'')))

f51960af7dd4813a587ab26388ddab3b28d1f7b4

number of bytes in a RIPEMD-160 digest: 20与上面的SHA256一样,我们再次看到了很多二进制操作的 "比特扰动器"。相当酷。好了,我们终于准备好获取我们的比特币地址了。我们将通过创建一个名为PublicKey的Point子类来使它变得更漂亮,同样,它只是Curve上的一个Point,但现在有一些额外的语义和对比特币公钥的解释,还有一些将密钥编码/解码为字节的方法,用于比特币协议的通信。class PublicKey(Point):

"""

The public key is just a Point on a Curve, but has some additional specific

encoding / decoding functionality that this class implements.

"""

@classmethod

def from_point(cls, pt: Point):

""" promote a Point to be a PublicKey """

return cls(pt.curve, pt.x, pt.y)

def encode(self, compressed, hash160=False):

""" return the SEC bytes encoding of the public key Point """

# calculate the bytes

if compressed:

# (x,y) is very redundant. Because y^2 = x^3 + 7,

# we can just encode x, and then y = +/- sqrt(x^3 + 7),

# so we need one more bit to encode whether it was the + or the -

# but because this is modular arithmetic there is no +/-, instead

# it can be shown that one y will always be even and the other odd.

prefix = b'\x02' if self.y % 2 == 0 else b'\x03'

pkb = prefix + self.x.to_bytes(32, 'big')

else:

pkb = b'\x04' + self.x.to_bytes(32, 'big') + self.y.to_bytes(32, 'big')

# hash if desired

return ripemd160(sha256(pkb)) if hash160 else pkb

def address(self, net: str, compressed: bool) -> str:

""" return the associated bitcoin address for this public key as string """

# encode the public key into bytes and hash to get the payload

pkb_hash = self.encode(compressed=compressed, hash160=True)

# add version byte (0x00 for Main Network, or 0x6f for Test Network)

version = {'main': b'\x00', 'test': b'\x6f'}

ver_pkb_hash = version[net] + pkb_hash

# calculate the checksum

checksum = sha256(sha256(ver_pkb_hash))[:4]

# append to form the full 25-byte binary Bitcoin Address

byte_address = ver_pkb_hash + checksum

# finally b58 encode the result

b58check_address = b58encode(byte_address)

return b58check_address我们还没有准备好让这个类使用,因为你会注意到这里还有一个必要的依赖,那就是b58编码函数b58encode。这只是一个比特币专用的字节编码,使用基数58,字母表中的字符是非常不明确的。例如,它不使用'O'和'0',因为它们在纸上很容易被弄乱。所以我们必须把我们的比特币地址(原始形式是25个字节)转换成基数58,然后打印出这些字符。我们地址的原始25个字节包含1个字节的版本(比特币 "主网 "是b'\x00',而比特币 "测试网 "使用b'\x6f'),然后是哈希摘要的20个字节,最后是4个字节的校验,这样我们就可以在用户在一些文本框中输入比特币地址时出错,概率为1-1/2**32=99.99999998%。所以这里是b58编码。# base58 encoding / decoding utilities

# reference: https://en.bitcoin.it/wiki/Base58Check_encoding

alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'

def b58encode(b: bytes) -> str:

assert len(b) == 25 # version is 1 byte, pkb_hash 20 bytes, checksum 4 bytes

n = int.from_bytes(b, 'big')

chars = []

while n:

n, i = divmod(n, 58)

chars.append(alphabet[i])

# special case handle the leading 0 bytes... ¯\_(ツ)_/¯

num_leading_zeros = len(b) - len(b.lstrip(b'\x00'))

res = num_leading_zeros * alphabet[0] + ''.join(reversed(chars))

return res让我们看看BTC地址:# we are going to use the develop's Bitcoin parallel universe "test net" for this demo, so net='test'

address = PublicKey.from_point(public_key).address(net='test', compressed=True)

print(address)

mnNcaVkC35ezZSgvn8fhXEa9QTHSUtPfzQ酷,我们现在可以检查一些区块探索者网站,以验证这个地址以前从未交易过:https://www.blockchain.com/btc-testnet/address/mnNcaVkC35ezZSgvn8fhXEa9QTHSUtPfzQ。在本教程结束时,它不会是这样的,但在写作时,我确实看到这个地址是 "干净的",所以到目前为止,没有人像我们上面做的那样在测试网上生成和使用秘密密钥。这是有道理的,因为一定有其他的 "Andrej "有不好的幽默感,也在捣鼓比特币。但我们也可以检查一些超级非秘密的密匙,我们预计这些密匙在过去会被人们使用。例如,我们可以检查属于最低有效秘钥1的地址,其中公钥正好是生成器点:)。下面是我们如何得到它。lol_secret_key = 1

lol_public_key = lol_secret_key * G

lol_address = PublicKey.from_point(lol_public_key).address(net='test', compressed=True)

lol_address

'mrCDrCybB6J1vRfbwM5hemdJz73FwDBC8r'事实上,我们在区块链浏览器上看到,在写这篇文章时,这个地址已经交易了1812次,余额为0.00美元BTC。这是有道理的,因为如果它确实有任何余额(在天真的情况下,加上我们将讨论的脚本语言的一些微妙之处),那么任何人都可以花费它,因为他们知道秘密密钥(1),并可以用它来数字签名花费它的交易。我们很快就会看到它是如何运作的。第一部分:到目前为止的总结我们能够生成一个加密身份,它包括一个只有我们知道的秘密密钥(一个随机整数),以及一个通过使用比特币椭圆曲线上的生成点的标量乘法在椭圆曲线上跳转而得出的公钥。然后,我们还得出了相关的比特币地址,我们可以与他人分享,向他们要钱,这样做涉及到两个哈希函数(SHA256和RIPEMD160)的引入。下面是总结出来的三个重要变量,并再次打印出来。print("Our first Bitcoin identity:")

print("1. secret key: ", secret_key)

print("2. public key: ", (public_key.x, public_key.y))

print("3. Bitcoin address: ", address)

Our first Bitcoin identity:

1. secret key: 22265090479312778178772228083027296664144

2. public key: (83998262154709529558614902604110599582969848537757180553516367057821848015989, 37676469766173670826348691885774454391218658108212372128812329274086400588247)

3. Bitcoin address: mnNcaVkC35ezZSgvn8fhXEa9QTHSUtPfzQ第2部分:获得启动资金+比特币介绍现在是时候创建一个交易了。我们将从上面生成的地址(mnNcaVkC35ezZSgvn8fhXEa9QTHSUtPfzQ)发送一些BTC到我们控制的第二个钱包。现在让我们创建这个第二个 "目标 "钱包。secret_key2 = int.from_bytes(b"Andrej's Super Secret 2nd Wallet", 'big') # or just random.randrange(1, bitcoin_gen.n)

assert 1 <= secret_key2 < bitcoin_gen.n # check it's valid

public_key2 = secret_key2 * G

address2 = PublicKey.from_point(public_key2).address(net='test', compressed=True)

print("Our second Bitcoin identity:")

print("1. secret key: ", secret_key2)

print("2. public key: ", (public_key2.x, public_key2.y))

print("3. Bitcoin address: ", address2)

Our second Bitcoin identity:

1. secret key: 29595381593786747354608258168471648998894101022644411052850960746671046944116

2. public key: (70010837237584666034852528437623689803658776589997047576978119215393051139210, 35910266550486169026860404782843121421687961955681935571785539885177648410329)

3. Bitcoin address: mrFF91kpuRbivucowsY512fDnYt6BWrvx9好的,我们的目标是将一些BTC从mnNcaVkC35ezZSgvn8fhXEa9QTHSUtPfzQ发送到mrFF91kpuRbivucowsY512fDnYt6BWrvx9。首先,因为我们刚刚从头生成这些身份,第一个地址上没有比特币。因为我们使用的是 "平行宇宙 "的开发者意图的比特币测试网络,我们可以使用多个可用的龙头之一来优雅地请求一些BTC。我是这样做的,在谷歌上搜索 "比特币测试网络龙头",点击第一个链接,并要求龙头发送一些比特币到我们的源地址mnNcaVkC35ezZSgvn8fhXEa9QTHSUtPfzQ。几分钟后,我们可以回到区块链浏览器,看到我们收到了硬币,在这种情况下是0.001 BTC。测试网有水龙头,但你当然不会在主网上找到它们 :) 你必须开一个Coinbase账户(会产生一个钱包),然后用美元购买一些BTC。在本教程中,我们将在测试网上工作,但我们所做的一切在主网上也能正常工作。现在,如果我们点击确切的交易ID,我们可以看到一堆额外的信息,这些信息涉及到比特币的核心,以及货币是如何在其中体现的。Transaction id .首先注意,每笔交易都有一个独特的ID/哈希值。在这个例子中,水龙头交易的ID是46325085c89fb98a4b7ceee44eac9b955f09e1ddc86d8dad3dfdcba46b4d36b2。正如我们将看到的,这只是一个交易数据结构的SHA256双哈希值(哈希值的哈希值),我们将看到很快被序列化为字节。在比特币中,双SHA256哈希值经常被用来代替单哈希值,以增加安全性,以减轻仅有的一轮SHA256的一些缺点,以及在旧版本的SHA(SHA-1)上发现的一些相关攻击。Inputs and Outputs .我们看到,水龙头交易有1个输入和2个输出。1个输入来自地址2MwjXCY7RRpo8MYjtsJtP5erNirzFB9MtnH,价值0.17394181 BTC。有2个输出。第二个输出是我们的地址,我们正好收到0.001 BTC。第一个输出是一些不同的,未知的地址2NCorZJ6XfdimrFQuwWjcJhQJDxPqjNgLzG,它收到了0.17294013 BTC,估计是由水龙头所有者控制。请注意,输入与输出并不完全相加。事实上,我们有0.17394181 - (0.001 + 0.17294013) = 0.00000168。这个 "变化 "的数额被称为交易费,这个费用被允许由比特币矿工索取,他将这笔交易纳入他们的区块,在这种情况下,区块2005500。你可以看到,这个区块有48个交易,而水龙头交易是其中之一! 现在,该费用作为一种经济激励,让矿工将该交易纳入他们的区块,因为他们可以保留变化。矿工的费用越高,交易就越有可能、越快出现在区块链上。如果费用高,我们会期待它被矿工急切地接受,并包括在下一个区块中。如果费用较低,该交易可能永远不会被包括在内,因为网络中还有许多其他愿意支付更高费用的交易在广播。因此,如果你是一个矿工,而且你有有限的空间投入到你的区块中--为什么要这样做?当我们做自己的交易时,我们必须确保包括这个矿工的小费,并支付 "市场费",我们会查到。在这个区块的情况下,我们可以看到,这个区块的矿工赚取的BTC总量是0.09765625 BTC,来自特殊的 "Coinbase "交易,每个矿工都被允许从一个空的输入发送到他们自己,然后总共0.00316119 BTC是总的费用奖励,在这个区块的所有47个非Coinbase交易中总结出来。Size .还请注意,这个交易(序列化)是249字节。对于这样一个简单的交易,这是一个相当平均的大小。Pkscript .最后请注意,第二个输出(我们的0.001 BTC)当你向下滚动到它的细节时,有一个 "Pkscript "字段,它显示。OP_DUP

OP_HASH160

4b3518229b0d3554fe7cd3796ade632aff3069d8

OP_EQUALVERIFY

OP_CHECKSIG这就是比特币的事情变得有点疯狂的地方。它有一个完整的基于堆栈的脚本语言,但除非你在做疯狂的multisig智能合约三重托管后翻(?),绝大多数的交易都使用极少数简单的 "特例 "脚本之一,就像这里的一个。现在我的眼睛只是把它当作标准的简单东西来看待。这个 "Pkscript "是这个特定产出的 "锁定脚本",里面有0.001个BTC。我们要花掉这个输出,并在我们即将进行的交易中把它变成一个输入。为了解锁这个输出,我们必须满足这个锁定脚本的条件。用英语来说,这个脚本是说,任何希望使用这个输出的交易必须满足两个条件。1)他们的公钥最好哈希到4b3518229b0d3554fe7cd3796ade632aff3069d8。2)交易的数字签名,验证为由该公钥的相关私钥产生。只有秘钥的拥有者才能够:1)提供完整的公钥,这将被检查为正确的哈希,以及2)创建数字签名,我们很快就会看到。顺便说一下,我们可以验证我们的公钥的哈希值当然是正确的,所以我们将能够把它包括在我们即将进行的交易中,而所有的采矿节点将能够验证条件(1)。非常早期的比特币交易的锁定脚本直接包含了公钥(而不是它的哈希值),后面是OP_CHECKSIG,但以这种稍微复杂的方式来做,可以保护哈希值后面的公钥,直到所有者想花钱,才会透露公钥的信息。(如果你想了解更多,请查阅p2pk与p2pkh交易)。PublicKey.from_point(public_key).encode(compressed=True, hash160=True).hex()

'4b3518229b0d3554fe7cd3796ade632aff3069d8'第三部分:精心设计我们的交易好了,现在我们要实际开始我们的交易。也就是说,我们目前的钱包有0.001 BTC,我们想把0.0005 BTC发送到我们的第二个钱包。为了实现这一目标,我们的交易将有一个确切的输入(=水龙头交易的第二个输出),和确切的两个输出。一个输出将发送到我们的第二个地址,其余的我们将发送回我们自己的地址。这里是需要理解的一个关键部分。这有点古怪。任何比特币交易的每一个输入/输出都必须是完全花费的。因此,如果我们拥有0.001个BTC,并想把它的一半送到其他地方,我们实际上必须把一半送到那里,另一半送回给我们。如果所有输出的总和低于所有输入的总和,该交易将被认为是有效的(所以我们不是在造钱)。剩余的部分将是 "变化"(费用),将由在工作证明中侥幸胜出的矿工得到,并将我们的交易纳入他们新开采的区块。让我们从交易输入数据结构开始。@dataclass

class TxIn:

prev_tx: bytes # prev transaction ID: hash256 of prev tx contents

prev_index: int # UTXO output index in the transaction

script_sig: Script = None # unlocking script, Script class coming a bit later below

sequence: int = 0xffffffff # originally intended for "high frequency trades", with locktime

tx_in = TxIn(

prev_tx = bytes.fromhex('46325085c89fb98a4b7ceee44eac9b955f09e1ddc86d8dad3dfdcba46b4d36b2'),

prev_index = 1,

script_sig = None, # this field will have the digital signature, to be inserted later

)前两个变量(prev_tx,prev_index)确定了我们要花费的特定输出。请注意,我们没有任何地方指定我们要花多少输出。我们必须将输出(或通常称为 "UTXO",即未用交易输出的简称)全部用完。一旦我们完整地消费了这个UTXO,我们就可以自由地将其价值 "分块 "成我们喜欢的任何数量的输出,并且可以选择将其中一些分块送回我们自己的地址。无论如何,在这种情况下,我们正在识别向我们发送比特币的交易,并且我们说我们打算花费的输出是在它的第1个索引。第0个索引去了由龙头控制的其他未知地址,我们将无法花费,因为我们没有控制它(我们没有私钥,将无法创建数字签名)。script_sig字段我们将在以后重新审视。这是数字签名的地方,用我们的私钥加密签署所需的交易,并有效地说:"我作为私钥的拥有者批准这项交易,公钥的哈希值为4b3518229b0d3554fe7cd3796ade632aff3069d8"。sequence在中本聪的原始比特币实现中,旨在提供一种 "高频交易 "的功能,但今天的用途非常有限,我们将大部分忽略。计算费用。很好,所以上面的数据结构引用了我们交易的输入(这里有一个输入)。现在让我们为我们交易的两个输出创建数据结构。为了了解交易费用的 "交易费",有许多网站可以提供,或者我们可以滚动浏览最近区块中的一些交易来了解情况。最近的一些交易(包括上面的交易)被打包成一个区块,甚至 <1 satoshi/byte(satoshi is 1e-8 of a bitcoin)。因此,让我们尝试用一个非常慷慨的费用,也许是10 sat/B,或0.0000001的总交易费用。在这种情况下,我们输入0.001 BTC = 100,000 sat,费用将是2,500 sat(因为我们的交易将是大约250字节),我们将发送50,000 sat到我们的目标钱包,剩下的(100,000 - 2,500 - 50,000 = 47,500)返回给我们。@dataclass

class TxOut:

amount: int # in units of satoshi (1e-8 of a bitcoin)

script_pubkey: Script = None # locking script

tx_out1 = TxOut(

amount = 50000 # we will send this 50,000 sat to our target wallet

)

tx_out2 = TxOut(

amount = 47500 # back to us

)

# the fee of 2500 does not need to be manually specified, the miner will claim it填充锁定脚本。我们现在要为这两个输出填充script_pubkey的 "锁定脚本"。从本质上讲,我们要指定每个输出的条件,在这些条件下,每个输出可以被一些未来的交易所花费。如前所述,比特币有丰富的脚本语言,有近100条指令,可以按顺序排列成各种锁定/解锁脚本,但在这里我们要使用我们在上面已经看到的超级标准和无处不在的脚本,它也被龙头用于支付我们。为了表明这两个输出的所有权,我们基本上要指定谁能花费输出的公钥哈希值。除了我们必须用 "丰富的脚本语言 "的衬托来装扮它。好了,我们开始吧。回想一下,当我们在比特币区块浏览器中看到水龙头交易中的锁定脚本时,它有这样的形式。输出的所有者的公钥哈希值被夹在一些比特币脚本语言的操作代码中,我们稍后会介绍这些代码。OP_DUP

OP_HASH160

4b3518229b0d3554fe7cd3796ade632aff3069d8

OP_EQUALVERIFY

OP_CHECKSIG我们需要创建这个相同的结构并将其编码为字节,但我们要用新主人的哈希值来交换公钥哈希值。操作代码(如OP_DUP等)都通过一个固定的模式被编码为整数。这里就是。def encode_int(i, nbytes, encoding='little'):

""" encode integer i into nbytes bytes using a given byte ordering """

return i.to_bytes(nbytes, encoding)

def encode_varint(i):

""" encode a (possibly but rarely large) integer into bytes with a super simple compression scheme """

if i < 0xfd:

return bytes([i])

elif i < 0x10000:

return b'\xfd' + encode_int(i, 2)

elif i < 0x100000000:

return b'\xfe' + encode_int(i, 4)

elif i < 0x10000000000000000:

return b'\xff' + encode_int(i, 8)

else:

raise ValueError("integer too large: %d" % (i, ))

@dataclass

class Script:

cmds: List[Union[int, bytes]]

def encode(self):

out = []

for cmd in self.cmds:

if isinstance(cmd, int):

# an int is just an opcode, encode as a single byte

out += [encode_int(cmd, 1)]

elif isinstance(cmd, bytes):

# bytes represent an element, encode its length and then content

length = len(cmd)

assert length < 75 # any longer than this requires a bit of tedious handling that we'll skip here

out += [encode_int(length, 1), cmd]

ret = b''.join(out)

return encode_varint(len(ret)) + ret

# the first output will go to our 2nd wallet

out1_pkb_hash = PublicKey.from_point(public_key2).encode(compressed=True, hash160=True)

out1_script = Script([118, 169, out1_pkb_hash, 136, 172]) # OP_DUP, OP_HASH160, , OP_EQUALVERIFY, OP_CHECKSIG

print(out1_script.encode().hex())

# the second output will go back to us

out2_pkb_hash = PublicKey.from_point(public_key).encode(compressed=True, hash160=True)

out2_script = Script([118, 169, out2_pkb_hash, 136, 172])

print(out2_script.encode().hex())

1976a91475b0c9fc784ba2ea0839e3cdf2669495cac6707388ac

1976a9144b3518229b0d3554fe7cd3796ade632aff3069d888ac好了,我们现在要通过指定公钥的哈希值(用脚本的操作码填充)来有效地声明我们交易的两个输出的所有者。当我们为输入端创建解锁脚本时,我们将看到这些锁定脚本如何为输出端工作。现在重要的是要明白,我们是通过确定一个特定的公钥哈希值来有效地宣布每个输出UTXO的所有者。有了上述的锁定脚本,只有拥有原始公钥(及其相关的秘钥)的人,才能使用UTXO。数字签名现在是重要的部分,我们要循环指定交易输入tx_in的script_sig,我们在上面跳过了。特别是,我们要制作一个数字签名,有效地说:"我,与参考交易的输出锁定脚本上的公钥哈希相关的私钥所有者,同意将这个UTXO作为这个交易的输入花费"。不幸的是,这又是比特币变得非常花哨的地方,因为你实际上只能签署交易的一部分,而一些签名可以从一些方面汇聚起来,以各种方式组合。正如我们上面所做的,我们将只涵盖(到目前为止)最常见的使用情况,即签署整个交易和,并专门构建解锁脚本,只满足上述确切形式的锁定脚本(OP_DUP,OP_HASH160,,OP_EQUALVERIFY,OP_CHECKSIG)。首先,我们需要创建一个纯字节的 "消息",我们将对其进行数字签名。在这种情况下,该消息是整个交易的编码。所以这就很尴尬了--整个交易还不能被编码成字节,因为我们还没有完成它! 它仍然缺少我们的签名,而我们仍在努力构建它。相反,当我们对希望签名的交易输入进行序列化时,规则是将script_sig的编码(我们没有这个编码,因为我们也只是试图产生它......)替换为这个输入所指向的交易输出的script_pubkey。所有其他交易输入的 script_sig 也被替换成一个空的脚本,因为这些输入可能属于许多其他的所有者,他们可以单独独立地贡献自己的签名。好吧,我不确定这是否有任何意义。所以让我们在代码中看看吧。我们需要最终的数据结构,即实际的Transaction,这样我们就可以把它序列化为字节信息。它主要是一个薄薄的TxIns列表和TxOuts列表的容器:输入和输出。然后我们为新的Tx类实现序列化,也为TxIn和TxOut类实现序列化,因此我们可以将整个交易序列化为字节。@dataclass

class Tx:

version: int

tx_ins: List[TxIn]

tx_outs: List[TxOut]

locktime: int = 0

def encode(self, sig_index=-1) -> bytes:

"""

Encode this transaction as bytes.

If sig_index is given then return the modified transaction

encoding of this tx with respect to the single input index.

This result then constitutes the "message" that gets signed

by the aspiring transactor of this input.

"""

out = []

# encode metadata

out += [encode_int(self.version, 4)]

# encode inputs

out += [encode_varint(len(self.tx_ins))]

if sig_index == -1:

# we are just serializing a fully formed transaction

out += [tx_in.encode() for tx_in in self.tx_ins]

else:

# used when crafting digital signature for a specific input index

out += [tx_in.encode(script_override=(sig_index == i))

for i, tx_in in enumerate(self.tx_ins)]

# encode outputs

out += [encode_varint(len(self.tx_outs))]

out += [tx_out.encode() for tx_out in self.tx_outs]

# encode... other metadata

out += [encode_int(self.locktime, 4)]

out += [encode_int(1, 4) if sig_index != -1 else b''] # 1 = SIGHASH_ALL

return b''.join(out)

# we also need to know how to encode TxIn. This is just serialization protocol.

def txin_encode(self, script_override=None):

out = []

out += [self.prev_tx[::-1]] # little endian vs big endian encodings... sigh

out += [encode_int(self.prev_index, 4)]

if script_override is None:

# None = just use the actual script

out += [self.script_sig.encode()]

elif script_override is True:

# True = override the script with the script_pubkey of the associated input

out += [self.prev_tx_script_pubkey.encode()]

elif script_override is False:

# False = override with an empty script

out += [Script([]).encode()]

else:

raise ValueError("script_override must be one of None|True|False")

out += [encode_int(self.sequence, 4)]

return b''.join(out)

TxIn.encode = txin_encode # monkey patch into the class

# and TxOut as well

def txout_encode(self):

out = []

out += [encode_int(self.amount, 8)]

out += [self.script_pubkey.encode()]

return b''.join(out)

TxOut.encode = txout_encode # monkey patch into the class

tx = Tx(

version = 1,

tx_ins = [tx_in],

tx_outs = [tx_out1, tx_out2],

)在我们对Transaction对象调用.encode并获得其内容的字节数以便我们可以签名之前,我们需要满足比特币的规则,即用这个输入指向的交易输出的script_pubkey替换script_sig的编码(我们没有这个编码,因为我们又只是想产生它...)。这里有一个链接,再次指向原始交易。我们试图在索引1处花费其输出,而script_pubkey又是。OP_DUP

OP_HASH160

4b3518229b0d3554fe7cd3796ade632aff3069d8

OP_EQUALVERIFY

OP_CHECKSIG这个特殊的Block Explorer网站不允许我们以原始(字节)的形式获得这个数据,所以我们将以脚本的形式重新创建这个数据结构。source_script = Script([118, 169, out2_pkb_hash, 136, 172]) # OP_DUP, OP_HASH160, , OP_EQUALVERIFY, OP_CHECKSIG

print("recall out2_pkb_hash is just raw bytes of the hash of public_key: ", out2_pkb_hash.hex())

print(source_script.encode().hex()) # we can get the bytes of the script_pubkey now

recall out2_pkb_hash is just raw bytes of the hash of public_key: 4b3518229b0d3554fe7cd3796ade632aff3069d8

1976a9144b3518229b0d3554fe7cd3796ade632aff3069d888ac

# monkey patch this into the input of the transaction we are trying sign and construct

tx_in.prev_tx_script_pubkey = source_script

# get the "message" we need to digitally sign!!

message = tx.encode(sig_index = 0)

message.hex()

'0100000001b2364d6ba4cbfd3dad8d6dc8dde1095f959bac4ee4ee7c4b8ab99fc885503246010000001976a9144b3518229b0d3554fe7cd3796ade632aff3069d888acffffffff0250c30000000000001976a91475b0c9fc784ba2ea0839e3cdf2669495cac6707388ac8cb90000000000001976a9144b3518229b0d3554fe7cd3796ade632aff3069d888ac0000000001000000'好吧,让我们停顿一下。我们已经将交易编码为字节,以创建一个 "消息",用数字签名的行话来说。想一想上述字节编码的内容,以及我们将要签署的是什么。我们通过参考以前特定交易的输出来确定这个交易的确切输入(这里当然只是一个输入)。我们也在识别这个交易的确切输出(可以说是新的即将被铸造的UTXO)以及它们的script_pubkey字段,在最常见的情况下,这些字段通过被包裹在Script中的公钥哈希声明每个输出的所有者。特别是,当我们签署一个特定的输入时,我们当然不包括任何其他输入的script_sig(你可以看到,txin_encode函数会将它们设置为空脚本)。事实上,在完全一般的情况下(虽然很少),我们甚至可能没有它们。所以这个消息真正编码的只是输入和新的输出,它们的数量,以及它们的所有者(通过锁定脚本指定每个所有者的公钥哈希)。我们现在准备用我们的私钥对消息进行数字签名。实际的签名本身是两个整数的元组(r,s)。就像上面的椭圆曲线加密法(ECC)一样,我将不涉及椭圆曲线数字签名算法(ECDSA)的全部数学细节。而只是提供代码,并说明它并不十分可怕。@dataclass

class Signature:

r: int

s: int

def sign(secret_key: int, message: bytes) -> Signature:

# the order of the elliptic curve used in bitcoin

n = bitcoin_gen.n

# double hash the message and convert to integer

z = int.from_bytes(sha256(sha256(message)), 'big')

# generate a new secret/public key pair at random

sk = random.randrange(1, n)

P = sk * bitcoin_gen.G

# calculate the signature

r = P.x

s = inv(sk, n) * (z + secret_key * r) % n

if s > n / 2:

s = n - s

sig = Signature(r, s)

return sig

def verify(public_key: Point, message: bytes, sig: Signature) -> bool:

# just a stub for reference on how a signature would be verified in terms of the API

# we don't need to verify any signatures to craft a transaction, but we would if we were mining

pass

random.seed(int.from_bytes(sha256(message), 'big')) # see note below

sig = sign(secret_key, message)

sig

Signature(r=47256385045018612897921731322704225983926443696060225906633967860304940939048, s=24798952842859654103158450705258206127588200130910777589265114945580848358502)在上面,你会注意到一个经常被评论的(也是非常正确的)微妙之处:在这种天真的形式下,当我们生成sk时,我们在签名过程中生成一个随机数。这意味着我们的签名在每次签名时都会改变,这在很多方面都是不可取的,包括这项工作的可重复性。情况很快就会变得更糟:如果你用相同的sk签署两个不同的信息,攻击者可以恢复秘钥,哎呀。问问Playstation 3的人就知道了。有一个特定的标准(称为RFC 6979)推荐了一种特定的方法来确定地生成sk,但为了简洁起见,我们在这里跳过它。相反,我在这里实现了一个简单的版本,我用信息的哈希值作为rng的种子。请不要在任何接近生产的地方使用这个方法。现在让我们来实现Signature的编码功能,这样我们就可以通过比特币协议来广播它。为了做到这一点,我们使用DER编码。def signature_encode(self) -> bytes:

""" return the DER encoding of this signature """

def dern(n):

nb = n.to_bytes(32, byteorder='big')

nb = nb.lstrip(b'\x00') # strip leading zeros

nb = (b'\x00' if nb[0] >= 0x80 else b'') + nb # preprend 0x00 if first byte >= 0x80

return nb

rb = dern(self.r)

sb = dern(self.s)

content = b''.join([bytes([0x02, len(rb)]), rb, bytes([0x02, len(sb)]), sb])

frame = b''.join([bytes([0x30, len(content)]), content])

return frame

Signature.encode = signature_encode # monkey patch into the class

sig_bytes = sig.encode()

sig_bytes.hex()

'30440220687a2a84aeaf387d8c6e9752fb8448f369c0f5da9fe695ff2eceb7fd6db8b728022036d3b5bc2746c20b32634a1a2d8f3b03f9ead38440b3f41451010f61e89ba466'我们终于准备好为我们交易的单一输入生成script_sig了。由于一个稍后将变得清晰的原因,它将包含两个元素。1)签名和2)公钥,两者都以字节为单位进行编码。# Append 1 (= SIGHASH_ALL), indicating this DER signature we created encoded "ALL" of the tx (by far most common)

sig_bytes_and_type = sig_bytes + b'\x01'

# Encode the public key into bytes. Notice we use hash160=False so we are revealing the full public key to Blockchain

pubkey_bytes = PublicKey.from_point(public_key).encode(compressed=True, hash160=False)

# Create a lightweight Script that just encodes those two things!

script_sig = Script([sig_bytes_and_type, pubkey_bytes])

tx_in.script_sig = script_sig好了,现在我们创建了锁定脚本(script_pubkey)和解锁脚本(script_sig),我们可以简单地思考一下这两个脚本在比特币脚本环境中是如何互动的。在高层次上,在采矿过程中的交易验证过程中,对于每个交易的输入,这两个脚本被拼接成一个脚本,然后在 "比特币虚拟机"(?) 我们现在可以看到,拼接这两个脚本的结果是这样的。

OP_DUP

OP_HASH160

OP_EQUALVERIFY

OP_CHECKSIG然后用一个典型的基于堆栈的push/pop方案从上到下执行,任何字节都被推入堆栈,任何操作都会消耗一些输入并推送一些输出。所以在这里我们把签名和pubkey推入堆栈,然后pubkey被复制(OP_DUP),被散列(OP_HASH160),散列与pubkey_hash_bytes比较(OP_EQUALVERIFY),最后数字签名的完整性被验证为已经被相关的私钥签署。现在我们已经完成了所有必要的步骤 让我们再看看我们完整构建的交易的再现。tx

Tx(version=1, tx_ins=[TxIn(prev_tx=b'F2P\x85\xc8\x9f\xb9\x8aK|\xee\xe4N\xac\x9b\x95_\t\xe1\xdd\xc8m\x8d\xad=\xfd\xcb\xa4kM6\xb2', prev_index=1, script_sig=Script(cmds=[b"0D\x02 hz*\x84\xae\xaf8}\x8cn\x97R\xfb\x84H\xf3i\xc0\xf5\xda\x9f\xe6\x95\xff.\xce\xb7\xfdm\xb8\xb7(\x02 6\xd3\xb5\xbc'F\xc2\x0b2cJ\x1a-\x8f;\x03\xf9\xea\xd3\x84@\xb3\xf4\x14Q\x01\x0fa\xe8\x9b\xa4f\x01", b'\x03\xb9\xb5T\xe2P"\xc2\xaeT\x9b\x0c0\xc1\x8d\xf0\xa8\xe0IR#\xf6\'\xae8\xdf\t\x92\xef\xb4w\x94u']), sequence=4294967295)], tx_outs=[TxOut(amount=50000, script_pubkey=Script(cmds=[118, 169, b'u\xb0\xc9\xfcxK\xa2\xea\x089\xe3\xcd\xf2f\x94\x95\xca\xc6ps', 136, 172])), TxOut(amount=47500, script_pubkey=Script(cmds=[118, 169, b'K5\x18"\x9b\r5T\xfe|\xd3yj\xdec*\xff0i\xd8', 136, 172]))], locktime=0)很轻便,不是吗?一笔比特币交易并没有那么多。让我们把它编码成字节并以十六进制显示。tx.encode().hex()

'0100000001b2364d6ba4cbfd3dad8d6dc8dde1095f959bac4ee4ee7c4b8ab99fc885503246010000006a4730440220687a2a84aeaf387d8c6e9752fb8448f369c0f5da9fe695ff2eceb7fd6db8b728022036d3b5bc2746c20b32634a1a2d8f3b03f9ead38440b3f41451010f61e89ba466012103b9b554e25022c2ae549b0c30c18df0a8e0495223f627ae38df0992efb4779475ffffffff0250c30000000000001976a91475b0c9fc784ba2ea0839e3cdf2669495cac6707388ac8cb90000000000001976a9144b3518229b0d3554fe7cd3796ade632aff3069d888ac00000000'

print("Transaction size in bytes: ", len(tx.encode()))

Transaction size in bytes: 225最后让我们计算一下我们完成的交易的ID。def tx_id(self) -> str:

return sha256(sha256(self.encode()))[::-1].hex() # little/big endian conventions require byte order swap

Tx.id = tx_id # monkey patch into the class

tx.id() # once this transaction goes through, this will be its id

'245e2d1f87415836cbb7b0bc84e40f4ca1d2a812be0eda381f02fb2224b4ad69'我们现在已经准备好向全世界的比特币节点广播交易。我们实际上是把定义我们交易的225个字节(嵌入到标准的比特币协议网络信封中)爆破出去。比特币节点将解码它,验证它,并把它纳入他们可能随时开采的下一个区块(如果费用足够高的话)。用汉语来说,这225个字节是在说 "你好,比特币网络,你好吗?很好。我想创建一个新的交易,把46325085c89fb98a4b7ceee44eac9b955f09e1ddc86d8dad3dfdcba46b4d36b2交易的输出(UTXO)放在索引1处,我想把它的金额分成两个输出。一个到地址mrFF91kpuRbivucowsY512fDnYt6BWrvx9,金额为50,000 sat,另一个到地址mnNcaVkC35ezZSgvn8fhXEa9QTHSUtPfzQ,金额为47,500 sat。(据了解,剩下的2,500 sat 将归属于任何将此交易纳入其区块的矿工)。这里有两份证明我可以花费这笔UTXO的文件:我的公钥,以及上述意向书的相关私钥所产生的数字签名。Kkthx!"我们要把这句话广播到网络上,看看它是否能运行下去! 我们可以在这里加入一个简单的客户端,通过套接字说比特币协议来与节点通信--我们先做握手(来回发送版本),然后用tx消息广播上面的交易字节。然而,这段代码有点长,而且不是超级令人兴奋的(它是按照比特币协议中描述的特定消息格式进行的大量序列化),所以我将使用blockstream的有用的tx/push端点来广播交易,而不是进一步膨胀这个笔记本。这只是一个大的文本框,我们复制粘贴原始交易的十六进制,就像上面一样,然后点击 "广播"。如果你想用原始比特币协议手动完成这个任务,你会想看看我的SimpleNode实现,用它来与一个节点进行套接字通信。import time; time.sleep(1.0) # now we wait :p, for the network to execute the transaction and include it in a block这里是交易! 我们可以看到,我们的原始字节被正确解析出来,交易被判定为有效,并被纳入2005515区块。我们的交易是这个区块中包含的31个交易之一,矿工为了感谢我们,要求支付我们的费用。把一切都放在一起。再进行一次合并交易现在让我们把所有东西放在一起,创造一个最后的身份,并把我们所有的剩余资金转移到这个钱包里。secret_key3 = int.from_bytes(b"Andrej's Super Secret 3rd Wallet", 'big') # or just random.randrange(1, bitcoin_gen.n)

assert 1 <= secret_key3 < bitcoin_gen.n # check it's valid

public_key3 = secret_key3 * G

address3 = PublicKey.from_point(public_key3).address(net='test', compressed=True)

print("Our third Bitcoin identity:")

print("1. secret key: ", secret_key3)

print("2. public key: ", (public_key3.x, public_key3.y))

print("3. Bitcoin address: ", address3)

Our third Bitcoin identity:

1. secret key: 29595381593786747354608258168471648998894101022644411057647114205835530364276

2. public key: (10431688308521398859068831048649547920603040245302637088532768399600614938636, 74559974378244821290907538448690356815087741133062157870433812445804889333467)

3. Bitcoin address: mgh4VjZx5MpkHRis9mDsF2ZcKLdXoP3oQ4并让我们伪造交易。我们目前在第一个钱包mnNcaVkC35ezZSgvn8fhXEa9QTHSUtPfzQ里有47,500个席位,在第二个钱包mrFF91kpuRbivucowsY512fDnYt6BWrvx9里有50,000个席位。我们将创建一个以这两个为输入的交易,并向第三个钱包mgh4VjZx5MpkHRis9mDsF2ZcKLdXoP3oQ4输出。像以前一样,我们将支付2500 sat作为费用,所以我们要给自己发送50,000 + 47,500 - 2500 = 95,000 sat。# ----------------------------

# first input of the transaction

tx_in1 = TxIn(

prev_tx = bytes.fromhex('245e2d1f87415836cbb7b0bc84e40f4ca1d2a812be0eda381f02fb2224b4ad69'),

prev_index = 0,

script_sig = None, # digital signature to be inserted later

)

# reconstruct the script_pubkey locking this UTXO (note: it's the first output index in the

# referenced transaction, but the owner is the second identity/wallet!)

# recall this information is "swapped in" when we digitally sign the spend of this UTXO a bit later

pkb_hash = PublicKey.from_point(public_key2).encode(compressed=True, hash160=True)

tx_in1.prev_tx_script_pubkey = Script([118, 169, pkb_hash, 136, 172]) # OP_DUP, OP_HASH160, , OP_EQUALVERIFY, OP_CHECKSIG

# ----------------------------

# second input of the transaction

tx_in2 = TxIn(

prev_tx = bytes.fromhex('245e2d1f87415836cbb7b0bc84e40f4ca1d2a812be0eda381f02fb2224b4ad69'),

prev_index = 1,

script_sig = None, # digital signature to be inserted later

)

pkb_hash = PublicKey.from_point(public_key).encode(compressed=True, hash160=True)

tx_in2.prev_tx_script_pubkey = Script([118, 169, pkb_hash, 136, 172]) # OP_DUP, OP_HASH160, , OP_EQUALVERIFY, OP_CHECKSIG

# ----------------------------

# define the (single) output

tx_out = TxOut(

amount = 95000,

script_pubkey = None, # locking script, inserted separately right below

)

# declare the owner as identity 3 above, by inserting the public key hash into the Script "padding"

out_pkb_hash = PublicKey.from_point(public_key3).encode(compressed=True, hash160=True)

out_script = Script([118, 169, out_pkb_hash, 136, 172]) # OP_DUP, OP_HASH160, , OP_EQUALVERIFY, OP_CHECKSIG

tx_out.script_pubkey = out_script

# ----------------------------

# create the aspiring transaction object

tx = Tx(

version = 1,

tx_ins = [tx_in1, tx_in2], # 2 inputs this time!

tx_outs = [tx_out], # ...and a single output

)

# ----------------------------

# digitally sign the spend of the first input of this transaction

# note that index 0 of the input transaction is our second identity! so it must sign here

message1 = tx.encode(sig_index = 0)

random.seed(int.from_bytes(sha256(message1), 'big'))

sig1 = sign(secret_key2, message1) # identity 2 signs

sig_bytes_and_type1 = sig1.encode() + b'\x01' # DER signature + SIGHASH_ALL

pubkey_bytes = PublicKey.from_point(public_key2).encode(compressed=True, hash160=False)

script_sig1 = Script([sig_bytes_and_type1, pubkey_bytes])

tx_in1.script_sig = script_sig1

# ----------------------------

# digitally sign the spend of the second input of this transaction

# note that index 1 of the input transaction is our first identity, so it signs here

message2 = tx.encode(sig_index = 1)

random.seed(int.from_bytes(sha256(message2), 'big'))

sig2 = sign(secret_key, message2) # identity 1 signs

sig_bytes_and_type2 = sig2.encode() + b'\x01' # DER signature + SIGHASH_ALL

pubkey_bytes = PublicKey.from_point(public_key).encode(compressed=True, hash160=False)

script_sig2 = Script([sig_bytes_and_type2, pubkey_bytes])

tx_in2.script_sig = script_sig2

# and that should be it!

print(tx.id())

print(tx)

print(tx.encode().hex())

361fbb9de4ef5bfa8c1cbd5eff818ed9273f6e1f74b41a7f9a9e8427c9008b93

Tx(version=1, tx_ins=[TxIn(prev_tx=b'$^-\x1f\x87AX6\xcb\xb7\xb0\xbc\x84\xe4\x0fL\xa1\xd2\xa8\x12\xbe\x0e\xda8\x1f\x02\xfb"$\xb4\xadi', prev_index=0, script_sig=Script(cmds=[b'0D\x02 \x19\x9aj\xa5c\x06\xce\xbc\xda\xcd\x1e\xba&\xb5^\xafo\x92\xebF\xeb\x90\xd1\xb7\xe7rK\xac\xbe\x1d\x19\x14\x02 \x10\x1c\rF\xe036\x1c`Ski\x89\xef\xddo\xa6\x92&_\xcd\xa1dgn/I\x88Xq\x03\x8a\x01', b'\x03\x9a\xc8\xba\xc8\xf6\xd9\x16\xb8\xa8[E\x8e\x08~\x0c\xd0~jv\xa6\xbf\xdd\xe9\xbbvk\x17\x08m\x9a\\\x8a']), sequence=4294967295), TxIn(prev_tx=b'$^-\x1f\x87AX6\xcb\xb7\xb0\xbc\x84\xe4\x0fL\xa1\xd2\xa8\x12\xbe\x0e\xda8\x1f\x02\xfb"$\xb4\xadi', prev_index=1, script_sig=Script(cmds=[b'0E\x02!\x00\x84\xecC#\xed\x07\xdaJ\xf6F \x91\xb4gbP\xc3wRs0\x19\x1a?\xf3\xf5Y\xa8\x8b\xea\xe2\xe2\x02 w%\x13\x92\xec/R2|\xb7)k\xe8\x9c\xc0\x01Qn@9\xba\xdd*\xd7\xbb\xc9P\xc4\xc1\xb6\xd7\xcc\x01', b'\x03\xb9\xb5T\xe2P"\xc2\xaeT\x9b\x0c0\xc1\x8d\xf0\xa8\xe0IR#\xf6\'\xae8\xdf\t\x92\xef\xb4w\x94u']), sequence=4294967295)], tx_outs=[TxOut(amount=95000, script_pubkey=Script(cmds=[118, 169, b'\x0c\xe1vI\xc10l)\x1c\xa9\xe5\x87\xf8y;[\x06V<\xea', 136, 172]))], locktime=0)

010000000269adb42422fb021f38da0ebe12a8d2a14c0fe484bcb0b7cb365841871f2d5e24000000006a4730440220199a6aa56306cebcdacd1eba26b55eaf6f92eb46eb90d1b7e7724bacbe1d19140220101c0d46e033361c60536b6989efdd6fa692265fcda164676e2f49885871038a0121039ac8bac8f6d916b8a85b458e087e0cd07e6a76a6bfdde9bb766b17086d9a5c8affffffff69adb42422fb021f38da0ebe12a8d2a14c0fe484bcb0b7cb365841871f2d5e24010000006b48304502210084ec4323ed07da4af6462091b4676250c377527330191a3ff3f559a88beae2e2022077251392ec2f52327cb7296be89cc001516e4039badd2ad7bbc950c4c1b6d7cc012103b9b554e25022c2ae549b0c30c18df0a8e0495223f627ae38df0992efb4779475ffffffff0118730100000000001976a9140ce17649c1306c291ca9e587f8793b5b06563cea88ac00000000我们再次前往Blockstream tx/push端点,复制粘贴上面的交易十六进制并等待:)import time; time.sleep(1.0)

# in Bitcoin main net a block will take about 10 minutes to mine

# (Proof of Work difficulty is dynamically adjusted to make it so)而这里就是这笔交易,因为它最终出现了,是区块2005671的一部分,同时还有其他25笔交易。给读者的练习:从我的第三个身份钱包(mgh4VjZx5MpkHRis9mDsF2ZcKLdXoP3oQ4)偷我的比特币到你自己的钱包;) 如果成功完成,第3个钱包将显示 "最终余额 "为0。在撰写本报告时,这是0.00095000 BTC,正如我们打算和预期的那样。这就是我们要总结的地方! 当然,这只是对比特币的简单的演示,它使用的是现在有点传统的P2PKH交易方式(而不是最近的创新,包括P2SH,Segwit,bech32,等等),当然我们也没有涵盖任何交易/区块验证,采矿,等等。然而,我希望这可以作为一个很好的介绍,让大家了解比特币的价值是如何体现的,以及密码学是如何用来保证交易安全的核心概念。从本质上讲,我们有一个UTXO的DAG,每个UTXO都有一定的数量和锁定脚本,交易完全消耗和创造UTXO,它们被矿工每10分钟打包成块。然后,经济学被用来通过工作证明实现去中心化:任何实体获得向链上添加新区块的概率与他们在网络总SHA256散列能力中的分数成正比。当我在写我的karpathy/cryptos库时,思考所有代码的去向是很有趣的。大部分的加密复杂性来自ECC、ECDSA和SHA256,这些都是行业内相对标准的,你永远不会想自己真正实现("不要自己滚蛋")。除此之外,交易、区块等的核心数据结构是相当直接的,但围绕着比特币协议有很多不光彩的细节,以及所有数据结构在字节间的序列化/反序列化。除此之外,比特币是一个活生生的,不断发展的代码库,它在不断发展新的功能,以继续扩大规模,进一步加强其安全性,同时保持完全向后兼容,以避免硬分叉。有时,尊重这些限制会导致一些相当棘手的结构,例如,我发现Segwit至少在美学上不是很讨人喜欢。其他时候,有大量的复杂性(例如,与脚本语言及其所有的操作代码),在大多数基本的点对点交易中很少使用。最后,我非常喜欢比特币的各种历史方面。例如,我发现非常有趣的是,一些原始的中本聪的错误仍然存在,例如,在如何调整挖矿难度(有一个偏离的错误,计算是基于2015年的区块,而不是2016年),或一些操作代码是如何的错误(例如,原始的multisig)。或者一些围绕高频交易的原始中本聪的想法(锁定时间/序列)仍然存在,但只在可能不完全有意的情况下找到有限的用途。比特币是一个代码库,有其他软件项目的所有挣扎,但没有能力打破传统功能(这需要一个硬分叉)。如果你想深入研究,我发现《掌握比特币》和《比特币编程》是非常有用的参考资料。我还在我的 repo karpathy/cryptos 中实现了上述所有内容的更简洁、分离、测试和更广泛的版本,如果你想在你自己的区块链旅程中使用它作为参考。我一定会把这个笔记本也上传到那里。哦,在Twitter上找到我。希望你能学到一些东西,而且这很有趣!发布于 2022-03-31 16:23比特币 (Bitcoin)Python密码​赞同 8​​1 条评论​分享​喜欢​收藏​申请转载​文章被以下专栏收录区块链从零开始记录自己学习区块

python程序实现BTC(比特币)挖矿的完整代码_python_脚本之家

python程序实现BTC(比特币)挖矿的完整代码_python_脚本之家

脚本之家

服务器常用软件

手机版

关注微信

快捷导航

软件下载

android

MAC

驱动下载

字体下载

DLL

源码下载

PHP

ASP.NET

ASP

JSP

软件编程

C#

JAVA

C 语言

Delphi

Android

网络编程

PHP

ASP.NET

ASP

JavaScript

在线工具

CSS格式化

JS格式化

Html转化为Js

数据库

MYSQL

MSSQL

oracle

DB2

MARIADB

CMS

PHPCMS

DEDECMS

帝国CMS

WordPress

常用工具

PHP开发工具

python

Photoshop

必备软件

网站首页

网页制作

网络编程

脚本专栏

脚本下载

数据库

服务器

电子书籍

操作系统

网站运营

平面设计

其它

媒体动画

电脑基础

硬件教程

网络安全

vbs

DOS/BAT

hta

htc

python

perl

游戏相关

VBA

远程脚本

ColdFusion

ruby

autoit

seraphzone

PowerShell

linux shell

Lua

Golang

Erlang

其它

您的位置:首页 → 脚本专栏 → python → python实现BTC(比特币)挖矿

python程序实现BTC(比特币)挖矿的完整代码

 更新时间:2021年01月20日 10:24:21   作者:Mr.Pan_学狂  

这篇文章主要介绍了python程序实现BTC(比特币)挖矿的完整代码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

区块链的作业是比特币挖矿程序python实现,我做完了跟大家分享一波。

完整代码如下:

#Author:Mr.Pan_学狂

#Finish_time:2020/11/22/22:34

import hashlib

import time

def find_gold():#参数D是难度系数

x = 0#算力初始值

Diffcult_number = str(input('请输入难度系数:'))

print('开始挖矿!!')

while True:

x += 1

string = 'hello'

start_string = str(x)

combine_string = start_string + string#字符串拼接

h = hashlib.sha256()#采用sha256加密

h.update(combine_string.encode('utf-8'))#utf-8编码后哈希加密

gold_hash = h.hexdigest()

#print(gold_hash)

startTime = time.perf_counter()

if gold_hash[0:len(Diffcult_number)] == Diffcult_number:#对比哈希值的x位与难度系数是否相同,x为难度系数

print('算力:{}'.format(x), '用时:{}'.format(startTime))

print('哈希值:%s' % gold_hash)#占位符

print('挖矿成功!!')

break

elif gold_hash[0:len(Diffcult_number)] != Diffcult_number:#哈希值前x位与难度系数不相同

print('算力:{}'.format(x),'用时:{}'.format(startTime))

print('哈希值:%s' % gold_hash)

print('没有挖到!!')

print()

continue

if __name__ == '__main__':#主函数,调用函数,程序出口。

find_gold()

我设置的难度规则系数是:‘0abd',若哈希值的最前面四位是'0abd'则显示挖矿成功!!否则,挖矿失败。经过一段时间后的运行结果,如下图:

我觉得随着时代的进步,矿机的升级,个人PC机(服务器)想要挖到比特币是很难的。并且,目前出现了专业的挖矿公司和挖矿团队,个人的算力就根本微不足道了。所以,现在就是租赁挖矿公司的服务器,根据挖到的成果分红。还有一个问题,就是环保问题,当矿机工作的时候会产生噪声,而且特别消耗电力,会排放大量的二氧化碳。我个人认为仅仅只是为了获得记账权的服务费,而去挖矿其实不是太理想,因为浪费了大量的资源,包括计算机的算力,能源等等。

到此这篇关于python程序实现BTC(比特币)挖矿的完整代码的文章就介绍到这了,更多相关python实现BTC(比特币)挖矿内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:使用C#编写自己的区块链挖矿算法linux服务器被植入ddgs、qW3xT.2挖矿病毒的处理实战记录iOS架构从 MVC、MVP 到 MVVM

python

BTC

比特币

挖矿

相关文章

Python zip函数打包元素实例解析这篇文章主要介绍了Python zip函数打包元素实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 2019-12-12

python调用系统中应用程序的函数示例这篇文章主要为大家介绍了python调用系统中应用程序详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪 2022-06-06

python3中for循环踩过的坑记录这篇文章主要给大家介绍了python3中for循环踩坑的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 2020-12-12

python opencv根据颜色进行目标检测的方法示例这篇文章主要介绍了python opencv根据颜色进行目标检测的方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 2020-01-01

python not运算符的实例用法在本篇文章里小编给大家整理了一篇关于python not运算符的实例用法,有需要的朋友们可以学习参考下。 2021-06-06

将Python字符串生成PDF的实例代码详解这篇文章主要介绍了将Python字符串生成PDF的实例代码,本文通过代码给大家介绍的非常详细,具有一定的参考借鉴价值 ,需要的朋友可以参考下 2019-05-05

Python如何读写字节数据这篇文章主要介绍了Python如何读写字节数据,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下 2020-08-08

C语言实现二叉搜索树的完整总结这篇文章主要介绍了C语言实现二叉搜索树的完整总结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 2021-04-04

python使用threading.Condition交替打印两个字符这篇文章主要为大家详细介绍了python使用threading.Condition交替打印两个字符,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 2019-05-05

Python通过future处理并发问题这篇文章主要介绍了Python通过future处理并发问题,非常不错,具有参考借鉴价值,需要的朋友可以参考下 2017-10-10

最新评论

大家感兴趣的内容

1Python入门教程 超详细1小时学会Python2Pycharm 2020最新永久激活码(附最新激活码和插件3Python 元组(Tuple)操作详解4Python 列表(List)操作方法详解5Python 字典(Dictionary)操作详解6Pycharm 2020年最新激活码(亲测有效)7python strip()函数 介绍8pycharm 使用心得(一)安装和首次使用9python中使用xlrd、xlwt操作excel表格详解10python 中文乱码问题深入分析

最近更新的内容

python中如何使用分步式进程计算详解一篇文章弄懂Python中的可迭代对象、迭代器和生成器Python字符串的字符转换、字符串劈分、字符串合并问题分析对python中数组的del,remove,pop区别详解手把手教你搭建python+selenium自动化环境(图文)Python 列表推导式与字典推导式的实现Django压缩静态文件的实现方法详析tensorflow入门:tfrecord 和tf.data.TFRecorpython计算机视觉opencv卡号识别示例详解python如何实现递归转非递归

常用在线小工具

关于我们 -

广告合作 -

联系我们 -

免责声明 -

网站地图 -

投诉建议 -

在线投稿

©CopyRight 2006-2024 JB51.Net Inc All Rights Reserved. 脚本之家 版权所有



探索 PyMiner: 深入挖掘比特币的潜力-CSDN博客

>

探索 PyMiner: 深入挖掘比特币的潜力-CSDN博客

探索 PyMiner: 深入挖掘比特币的潜力

gitblog_00048

于 2024-03-15 09:48:50 发布

阅读量82

收藏

1

点赞数

3

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/gitblog_00048/article/details/136730636

版权

探索 PyMiner: 深入挖掘比特币的潜力

如果你对区块链技术和比特币感兴趣,并且希望通过编写代码深入了解其工作原理,那么 PyMiner 将是一个不错的选择。

什么是 PyMiner?

PyMiner 是一个基于 Python 的比特币挖矿工具,由 Jeff Garzik 创作并开源。该项目旨在帮助开发者和研究人员更好地理解比特币的核心算法和数据结构。通过使用 PyMiner,你可以探索如何实现自己的挖矿软件或进行其他与比特币相关的实验。

https://gitcode.com/jgarzik/pyminer?utm_source=artical_gitcode

能用来做什么?

PyMiner 可以用于:

学习比特币的工作原理:通过查看和运行项目的源代码,你可以深入了解比特币的共识机制、交易验证、区块生成等核心功能。开发自定义挖矿软件:PyMiner 提供了一些基本的挖矿模块,你可以在此基础上开发自己的挖矿程序。进行实验性研究:利用 PyMiner,你可以模拟不同的网络环境,测试新的区块链概念和技术。

特点

PyMiner 具有以下特点:

简洁易懂的代码:项目中的 Python 代码清晰明了,便于阅读和学习。支持多个比特币客户端:PyMiner 可以与其他 Bitcoin Core 客户端一起使用,包括 RPC API 集成。可扩展性:你可以根据需要添加新功能或修改现有模块,以满足特定需求。

如何开始使用?

要开始使用 PyMiner,首先确保你的系统上已安装了 Python 和 Git。然后,按照以下步骤克隆项目并将依赖项安装到虚拟环境中:

$ git clone https://gitcode.com/jgarzik/pyminer.git

$ cd pyminer

$ python -m venv .venv

$ source .venv/bin/activate

$ pip install -r requirements.txt

接下来,你可以尝试运行示例脚本,如 mine.py 或 getwork.py,了解 PyMiner 的基本功能。

$ python mine.py

$ python getwork.py

结论

PyMiner 是一个强大的工具,为那些希望深入研究比特币的人提供了宝贵的资源。无论你是想了解比特币的基本工作原理还是想要开发自定义的挖矿软件,都可以借助 PyMiner 实现目标。

https://gitcode.com/jgarzik/pyminer?utm_source=artical_gitcode

现在就开始探索 PyMiner,进一步了解比特币的魅力吧!

优惠劵

gitblog_00048

关注

关注

3

点赞

1

收藏

觉得还不错?

一键收藏

打赏

知道了

0

评论

探索 PyMiner: 深入挖掘比特币的潜力

探索 PyMiner: 深入挖掘比特币的潜力如果你对区块链技术和比特币感兴趣,并且希望通过编写代码深入了解其工作原理,那么 PyMiner 将是一个不错的选择。什么是 PyMiner?PyMiner 是一个基于 Python 的比特币挖矿工具,由 Jeff Garzik 创作并开源。该项目旨在帮助开发者和研究人员更好地理解比特币的核心算法和数据结构。通过使用 PyMiner,你可以探索如何实...

复制链接

扫一扫

比特币白皮书:一种点对点的电子现金系统(中文版)

01-05

文件格式:Word 总页数:17 目录: 1. 简介 2. 交易(Transactions) 3. 时间戳服务器(Timestamp server) 4. 工作量证明(Proof-of-Work) 5. 网络 6. 激励 7. 回收硬盘空间 8. 简化的支付确认(Simplified Payment ...

创世纪:比特币诞生记

聚集机器学习、信息安全

05-21

4358

2023年已经是比特币诞生的第15个年头,比特币似乎已逐渐淡出人们的视线,但其价格依然居高不下,一枚比特币的价格已接近人民币20万元,最近的巴厘岛情侣遇害事件更是让人们回忆起了币圈的种种传奇。本文对比特币进行考古,让大家了解比特币最初诞生的过程。

参与评论

您还未登录,请先

登录

后发表或查看评论

比特币与区块链的大众科普文

热门推荐

Netfilter

03-31

1万+

前言本文相对稍微长一点。 近日不知道为什么,区块链重新又火了起来,自2013年结识这个概念到现在已经4年了,最近由于大家都在聊这个,我也就想参与进来,然而我发现这是一个很难学习的东西,我一直不明白为什么就没有人能把区块链的技术讲明白,不管是各种博客文章,书籍以及知乎上那种带有抑扬顿挫感的装逼文,目的都好似在说区块链技术多么牛逼,多么难,而作者自己学会了,从而印证作者多么牛逼,多么牛。

基于requests爬取比特币信息并绘制价格走势图

01-12

本期内容:基于requests爬取比特币信息并绘制价格走势图 实验环境 - anaconda丨pycharm - python3.11.4 - requests 安装requests库的命令: pip install -i https://pypi.tuna.tsinghua.edu.cn/simple requests ...

MCM美国大学生数学建模竞赛题目与答案:比特币和黄金如何随时间上涨和下跌.pdf

08-21

比特币和黄金都是可替代的价值储存。除此之外,它们的不同。千百年来,人类一直使用黄金作为价值储存手段,而比特币(BTC)在 2010 年 5 月 22 日首次作为货币使用,当时拉斯洛-哈耶茨(Laszlo Hanyecz)用 10,000 ...

案例十 python 深度学习 RNN 预测比特币价格

08-11

【案例十】 python 深度学习 RNN 预测比特币价格

比特币0.9.2版编译中文手册.pdf

07-03

。。。

Matlab 实现比特币价格分析-1

08-04

分析2017年-2023年的交易数据 价格趋势、移动平均线、波动率和交易量分析 代码如下: % 1、数据读取 data = readtable('BTCUSDT.csv'); date = datetime(data.Date, 'InputFormat', 'yyyy/MM/dd');...

2021全球金融科技行业报告:区块链、比特币和数字金融-JP 摩根-2021218-86页.pdf

07-26

2021全球金融科技行业报告:区块链、比特币和数字金融-JP 摩根-2021218-86页.pdf

比特币价格预测系统的项目的源码和数据集

01-13

专栏《金融大模型实战》中(11-4)比特币价格预测系统的源码和数据集,专栏地址是:https://blog.csdn.net/asd343442/category_12531576.html?spm=1001.2014.3001.5482,欢迎大家观看,本专栏会持续更新,和大家共同...

比特币基于LSTM的多因子交易策略Python源码+文档说明

最新发布

02-12

比特币基于LSTM的多因子交易策略Python源码+文档说明 此策略会减少最大回撤,减少波动,比较稳健; 收益率与LSTM预测的准确率直接相关,应该加入更多因子,及设置更合适的超参 此模型同样的数据收益率存在随机性 ...

比特币白皮书:一种点对点的电子现金系统

05-22

比特币白皮书:一种点对点的电子现金系统 比特币白皮书:一种点对点的电子现金系统 比特币白皮书:一种点对点的电子现金系统

java创建比特币钱包

10-20

Java创建比特币钱包可以使用BitcoinJ这个库。BitcoinJ是一个Java实现的比特币协议库,可以用于创建比特币钱包、发送和接收比特币交易等。以下是创建比特币钱包的步骤:

1. 添加BitcoinJ依赖到项目中。

2. 创建一个NetworkParameters对象,用于指定比特币网络的参数。

3. 创建一个Wallet对象,用于存储比特币地址和私钥。

4. 生成一个新的比特币地址和私钥,并将其添加到钱包中。

5. 将钱包保存到本地文件中,以便下次使用。

以下是一个简单的Java代码示例,用于创建比特币钱包:

```

// 添加BitcoinJ依赖

org.bitcoinj

bitcoinj-core

0.15.7

// 创建NetworkParameters对象

NetworkParameters params = MainNetParams.get();

// 创建钱包

Wallet wallet = new Wallet(params);

// 生成新的比特币地址和私钥

ECKey key = new ECKey();

Address address = key.toAddress(params);

// 将地址和私钥添加到钱包中

wallet.importKey(key);

// 将钱包保存到本地文件中

File file = new File("wallet.dat");

wallet.saveToFile(file);

```

“相关推荐”对你有帮助么?

非常没帮助

没帮助

一般

有帮助

非常有帮助

提交

gitblog_00048

CSDN认证博客专家

CSDN认证企业博客

码龄23天

暂无认证

42

原创

-

周排名

-

总排名

9131

访问

等级

155

积分

157

粉丝

155

获赞

0

评论

194

收藏

私信

关注

热门文章

TestFixtures: Python 测试的得力助手

523

推荐文章:SVHTTPRequest - 简单易用的网络请求库

446

Assetic - 提升你的 Web 应用程序的静态资源管理能力

408

MonsterWM是一个轻量级的X窗口管理器,它注重速度和可定制性。通过提供基本的窗口管理和工作区管理功能,MonsterWM可以让您在Linux或Unix系统上实现高效的桌面环境。...

288

RythmEngine: 高效、灵活的Java模板引擎

346

您愿意向朋友推荐“博客详情页”吗?

强烈不推荐

不推荐

一般般

推荐

强烈推荐

提交

最新文章

jQuery Rails

探索数据清洗的新方法: DataGristle

AES-Python: Python实现的AES加密算法库

2024

03月

42篇

目录

目录

最新文章

jQuery Rails

探索数据清洗的新方法: DataGristle

AES-Python: Python实现的AES加密算法库

2024

03月

42篇

目录

评论

被折叠的  条评论

为什么被折叠?

到【灌水乐园】发言

查看更多评论

添加红包

祝福语

请填写红包祝福语或标题

红包数量

红包个数最小为10个

红包总金额

红包金额最低5元

余额支付

当前余额3.43元

前往充值 >

需支付:10.00元

取消

确定

下一步

知道了

成就一亿技术人!

领取后你会自动成为博主和红包主的粉丝

规则

hope_wisdom 发出的红包

打赏作者

gitblog_00048

你的鼓励将是我创作的最大动力

¥1

¥2

¥4

¥6

¥10

¥20

扫码支付:¥1

获取中

扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元

使用余额支付

点击重新获取

扫码支付

钱包余额

0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。 2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

一文看懂怎样用 Python 创建比特币交易-腾讯云开发者社区-腾讯云

怎样用 Python 创建比特币交易-腾讯云开发者社区-腾讯云区块链大本营一文看懂怎样用 Python 创建比特币交易关注作者腾讯云开发者社区文档建议反馈控制台首页学习活动专区工具TVP最新优惠活动文章/答案/技术大牛搜索搜索关闭发布登录/注册首页学习活动专区工具TVP最新优惠活动返回腾讯云官网区块链大本营首页学习活动专区工具TVP最新优惠活动返回腾讯云官网社区首页 >专栏 >一文看懂怎样用 Python 创建比特币交易一文看懂怎样用 Python 创建比特币交易区块链大本营关注发布于 2018-05-10 17:51:233.6K0发布于 2018-05-10 17:51:23举报文章被收录于专栏:区块链大本营区块链大本营比特币价格的上上下下,始终撩动着每一个人无比关切的小心脏。从去年初的 800 美元左右,飞涨到去年底到 19783.21 美元最高点,不到1年,便有将近 25 倍的升值速度。尽管眼下又掉回 8000 多美元的价格,但价格差不多能搞出去年同期一个数量级,币圈人士“过去一年比以往 10 年挣的都多”,已经是不争的事实。而对区块链开发者来说,据说也已经有拿到年新 500 万的天价。所以“跑步进入区块链”,已经成为不少程序员的共识。但是看过很多远离,我们如何才能迅速上手呢?国外网友 Ken Shirriff 在博客中分享了他在手动茶古剑比特币交易时的代码与对比特币协议的心得,区块链大本营编译如下。近期,媒体行业对比特币表现出极大的热情,这鼓舞着我从网络底层的数据流开始,认真学习比特币的工作原理。通常人们会使用钱包软件来进行比特币交易,钱包软件在方便用户的同时,向用户隐藏了比特币的交易流程,而我想亲自动手来体验比特币交易,我的目标是用Python手动创建一笔比特币交易,以十六进制数据的形式将交易广播到比特币网络中,然后观察这笔交易是怎么被加入到区块链中的。事实证明,这个过程很有趣,希望你也对它感兴趣。在本篇文章中,首先我会对比特币进行一个简单的概述,之后,我会从以下几个方面带领你们学习比特币:创建一个比特币地址(比特币中的账户),进行一笔比特币交易,签署交易,将交易广播到比特币网络中,最后等待交易的确认。比特币简述:

首先,我会介绍一下比特币系统是怎么运转的,然后再深入探讨整个细节。比特币是一个基于点对点网络的电子货币,你可以用现金在网上购买比特币,用比特币向他人转账,在有些商家,你可以像使用支付宝一样使用比特币付款,当然,你也可以卖出所持有的比特币换回现金。简而言之,在比特币网络中,分布式账本(区块链)记录并随时更新着每个比特币的所有权。与银行不同的是,比特币并没有与个人或个人的账户绑定,相反的,比特币只属于一个个比特币地址,比如:1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa。这里你可能已经绕晕了,难道这段字符中藏着比特币?当然不是,比特币地址是比特币网络中的一个身份,也可以通俗地说是你在比特币中开的一个“银行账户”,我们用这个“账户”来进行交易。在网站:blockchain.info中,你可以查到所有的交易信息: 比特币账户信息但是怎么证明这个账户是我的呢,不急,先往下看,你的疑问我会为你一一解答。比特币交易

如何像使用现金一样使用比特币呢?答案是创建一笔交易。在一笔交易中,比特币的所有者(上文提到过比特币的所有者是比特币地址)将所有权转移到一个新的比特币地址。比特币的一个颠覆性创新就是通过鼓励节点记账(也叫矿工挖矿),将交易记录放在一个分布式的数据库中。交易被集合在区块中,大概每十分钟比特币网络中产生一个新的区块,成为交易记录的一部分,称为区块链。加入到区块链中的交易可以被认为是一笔成功的交易。现在问题来了,谁来给你记账呢?是矿工,矿工的挖矿过程就是在往区块链中记账,矿工要核实每笔交易是否正确,核实完后,矿工们就开始算一道很难的数学题(密码学中的哈希函数),最早算出答案的人就能生成一个区块,也叫挖出了一个新的区块,这个区块将成为区块链的新一部分。也许你会问了,明明是记账,干着会计的活,为什么要叫挖矿呢?和传统的在地下挖矿石一样,比特币挖矿也是会有收获的。挖矿是一种新发行比特币的过程,当前,每挖到一个矿,矿工会得到系统奖励的12.5个比特币,按目前一个比特币接近一万美元的市价,这就是一笔12.5万美元的巨款。此外,矿工还可以获得本区块中所有的交易费,举例来说,在高度为512587的区块中,幸运的矿工总共收获了12.829个比特币。正因如此,矿工之间的竞争十分激烈,采矿的难度与矿工间激烈的竞争是比特币安全的重要保证,因为这样可以保证没有坏人能操纵系统。点对点网络

比特币并没有一个中央服务器,相反,比特币在一个点对点网络中运行。如果你运行一个比特币节点,那你就成了网络的一部分。比特币网络中的节点彼此交换自己存储的交易,区块,以及IP地址信息(用于节点间建立连接互相通信)。当你第一次连接到比特币网络,你的节点会从随机挑选的节点中下载区块链的信息。反过来,你的节点也会向后加入者提供信息。当你要创建一笔比特币交易时,你要把这笔交易发送给一些节点,这些节点会在比特币网络中广播这笔交易,直到全网都收到这笔交易。矿工们会收集你的交易信息,生成一个含有你这笔交易的区块,向全网广播,这时,你的节点也会收到这个区块信息,通过验证,这笔交易被加入到了区块链中,你就交易成功了。 加密技术

现在回到证明比特币账户是谁的这个问题。比特币使用数字签名技术以确保只有比特币账户的所有者才能使用账户中的比特币。比特币地址的所有者拥有与该地址相匹配的私钥,当花费比特币时,你用要这个私钥在交易上签名,证明自己是这个账户的所有者。这有点像现实生活中的盖章,盖章就意味着授权。怎么验证呢,公钥与比特币账户相关联,用公钥就可以验证签名是否正确。这样就解决了比特币账户是谁的这个问题。怎么来区分不同的交易呢?交易和区块都使用密码学上的哈希值进行索引,是不是有点耳熟,对,在比特币协议中,多处使用到了哈希函数,矿工们刚才算的数学题就是在算哈希函数。其实比特币并不长这个样

比特币协议探究

在接下来的文章里,我将逐步介绍我是怎样手动进行一次比特币交易的。首先,我生成了一个比特币账户以及对应的公钥,私钥。接下来我发起了一笔比特币交易,我向这个新生成的账户转了一小笔比特币。期间手动签署这笔交易很困难,它花费了我很多的时间。最后,我将这笔交易发送到比特币网络,等待它被加入区块链。本文的其余部分会详细地介绍这些步骤。事实证明,手动进行比特币交易比我想象中的更加困难。正如你所看到的,比特币的协议有些许混乱:它使用了大端格式数字(高位编址,将高序字节存储在起始地址),小端格式数字(低位编址,将低序字节存储在起始位置),固定长度数字,可变长度数字,自定义编码格式,DER编码格式以及各种加密算法。因此,仅仅是将数据转换为正确的格式就浪费了很多时间。我遇到的第二个难题就是加密,尝试一下手动加密,你就会发现密码学对人们多不友好,甚至可以说是无情。即使你只输错了一个字节,交易就会因出错被拒绝,而且它不会告诉你哪里出错了,你只能重来。最后,手动签署交易的过程也比想象中难得多,签署交易时每个环节都必须零失误,要么又要退回重来。比特币地址和密钥

第一步,我创建了一个比特币地址。通常情况下,人们都是使用比特币客户端软件来创建比特币地址和与之相关的密钥。本着学习的态度,我写了一些Python代码来生成比特币地址,从而揭示地址创建的机理。比特币使用了一系列的密钥和地址,下图解释了它们的关系。首先你要创建一个随机的256位的私钥,这个私钥用于在花费比特币时签署交易。因此,私钥必须保密,否则你的比特币可能会被盗用。椭圆曲线数字签名算法(Elliptic Curve Digital Signature Algorithm,ECDSA,美国政府的标准,接下来我们会讨论它)会从私钥中生成一个512位的公钥,这个公钥用于验证交易的签名。但不方便的是,比特币协议中需要在这个公钥上添加了前缀04,这个公钥在交易签署之前不会被泄露,不像其它系统中公钥就是为了公之于众的。比特币地址与公钥的关系

下一步就是生成与他人交易时使用的比特币地址了。512位的公钥太长不方便使用,因此使用SHA-256和RIPEMD哈希算法将其缩小为160位。然后使用比特币定义的Base58Check 编码将密钥编码为ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)格式。得到的地址(例如上文中的:1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa)就是你接收别人比特币时要发布的地址。需要注意的是,你无法从比特币地址中复原出公钥或私钥。如果你丢失了你的私钥(比如说你把私钥存在你的硬盘上,但硬盘丢失),你的比特币将永远丢失。最后,钱包交换格式密钥(WIF)用于将私钥添加到你的钱包软件中,这只是将私钥进行Base58Check编码转换为ASCII格式,这一步是可逆的,而且很容易经过逆变换恢复出256位的私钥。(图中有我的私钥,我很好奇是否有人会用我的私钥去偷(通过私钥签署交易,从而转走)我那价值80美分的比特币,然而真有人那么做了,可以在https://blockchain.info/address/1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa 看到,也算是为教学做贡献了。)总之,共有三种密钥:私钥,公钥,公钥的哈希值,经过使用Base58Check编码,它们对外都是以ASCII格式表示。私钥是其中最重要的密钥,因为花费比特币时需要私钥签署交易,而且其他的密钥都可以从私钥中产生。公钥的哈希值就是你们刚看的的比特币地址。我使用下面的代码片段来生成WIF格式的私钥和地址。私钥只是一个随机的256位的数字,使用椭圆曲线数字签名算法从私钥中生成公钥,公钥使用SHA-256算法,RIPEMD-160算法进行哈希计算,再经Base58编码并进行校验后得到比特币地址。最后,私钥用Base58Check编码以生成用于将私钥输入钱包软件的WIF编码。注意,这段Python随机函数代码在密码学上安全性并不高,如果你想要尝试这一步骤,建议使用更安全的钱包软件来生成比特币地址和密钥。def privateKeyToWif(key_hex):

return utils.base58CheckEncode(0x80, key_hex.decode('hex')) def privateKeyToPublicKey(s):

sk = ecdsa.SigningKey.from_string(s.decode('hex'), curve=ecdsa.SECP256k1)

vk = sk.verifying_key

return ('\04' + sk.verifying_key.to_string()).encode('hex')

def pubKeyToAddr(s):

ripemd160 = hashlib.new('ripemd160')

ripemd160.update(hashlib.sha256(s.decode('hex')).digest())

return utils.base58CheckEncode(0, ripemd160.digest())

def keyToAddr(s):

return pubKeyToAddr(privateKeyToPublicKey(s))

# Warning: this random function is not cryptographically strong and is just for example

private_key = ''.join(['%x' % random.randrange(16) for x in range(0, 64)])

print keyUtils.privateKeyToWif(private_key)

print keyUtils.keyToAddr(private_key)复制keyUtils.py从内部分析一笔交易

交易是比特币系统的基本操作,也许你会认为交易就是简单地把比特币从一个地址转移到另一个地址,但交易其实并不简单。一笔交易包含一个或多个输入和输出,交易中的每个输入的地址都提供比特币,每个输出的地址都接受比特币。一笔简单的比特币交易,交易C花费了从交易A和交易B获得的0.008个比特币,其中0.001个比特币被当作交易费付给矿工

上图显示了一笔简单的比特币交易“C”,在这笔交易中,有0.005个比特币是在交易A中获得的,0.003个比特币是在交易B中获得的。(图中箭头是由新交易的输入指向得到这些比特币的交易的输出,所以比特币的流向是逆着箭头方向的。)对于输出,有0.003个比特币给了第一个比特币地址,有0.004个比特币给了第二个比特币地址,剩余的0.001个比特币作为交易费付给矿工。请注意,本次交易并没有影响到在交易A中另一个输出为0.015的比特币。 在一笔交易中,输入的比特币地址必须花出所有的比特币,假如你在之前的交易收到了100个比特币,但你只想花1个比特币,创建这笔交易你必须花完所有的100个比特币,那剩下的99个比特币怎么办呢?解决方案就是在交易中再增加一个输出,将剩余的99个比特币转给自己。这样你就可以花费任意数额的比特币。 通常交易要支付交易费,如果一笔交易中输入的比特币总和大于输出的比特币的总和,剩余的费用就是给矿工的交易费。这笔费用并没有明确要求,但是对于矿工而言,没有交易费的交易就会被列为低优先级交易,可能要等上几天才会被处理甚至被矿工直接丢弃。交易费通常并不高,但它可能影响着你的交易。手动创建一笔交易

如下图所示,在我的实验中我发起了一笔只有一个输入一个输出的交易。我在Coinbase上买了一些比特币,并将0.00101234个比特币放入地址:1MMMMSUb1piy2ufrSguNUdFmAcvqrQF8M5中,这笔交易哈希为:81b4c832d70cb56ff957589752eb4125a4cab78a25a8fc52d6a09e5bd4404d48,我的目标是创建一笔交易,将这些比特币转入我的另一个地址:1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa,扣除0.0001个比特币的交易费后,目标地址将获得0.00091234个比特币。比特币交易结构实例 Blockchain.info上的交易记录https://blockchain.info/address/1MMMMSUb1piy2ufrSguNUdFmAcvqrQF8M5?filter=4按照协议标准,创建这笔交易很简单。如下表所示,这笔交易只有一个输入,源自于81b4c832d70cb56ff957589752eb4125a4cab78a25a8fc52d6a09e5bd4404d48中的输出0(第一个输出)。输出为0.00091234个比特币(91234在十六进制中用0x016462表示),它以小端格式存储在值区域中。加密过程中的scriptSig和scriptPubKey较为复杂,我们稍后再做讨论。version01 00 00 00input count01inputprevious output hash(reversed)48 4d 40 d4 5b 9e a0 d6 52 fc a8 25 8a b7 ca a4 25 41 eb 52 97 58 57 f9 6f b5 0c d7 32 c8 b4 81previous output index00 00 00 00script lengthscriptSigscript containing signaturesequenceff ff ff ffoutput count01outputvalue62 64 01 00 00 00 00 00script lengthscriptPubKeyscript containing destination addressblock lock time00 00 00 00这是我生成交易使用的代码,这段代码只是把数据打包成二进制文件。签署交易较为困难,我们等一会儿再说。# Makes a transaction from the inputs# outputs is a list of [redemptionSatoshis, outputScript]

def makeRawTransaction(outputTransactionHash, sourceIndex, scriptSig, outputs):

def makeOutput(data):

redemptionSatoshis, outputScript = data

return (struct.pack("

'%02x' % len(outputScript.decode('hex')) + outputScript)

formattedOutputs = ''.join(map(makeOutput, outputs))

return (

"01000000" + # 4 bytes version

"01" + # varint for number of inputs

outputTransactionHash.decode('hex')[::-1].encode('hex') + # reverse outputTransactionHash

struct.pack('

'%02x' % len(scriptSig.decode('hex')) + scriptSig +

"ffffffff" + # sequence

"%02x" % len(outputs) + # number of outputs

formattedOutputs +

"00000000" # lockTime

)复制txnUtils.py比特币交易怎样签署

下图为我们简单描述了交易是如何签署并相互连接的。针对中间这笔从比特币地址B转账到比特币地址C的交易。交易的内容(包括前一个交易的哈希值(索引))被进行哈希计算并用B的私钥签名。另外,B的公钥也被包含在了交易中。 通过执行几个简单运算,任何人都能验证B是否签署了这笔交易。首先,B的公钥与之前收到这笔比特币交易的地址做验证,证明B的公钥有效。(正如前面所说的,地址很容易从公钥中计算获得)。接下来,可以通过B的公钥验证B交易签名的真伪。这些步骤能确保交易的有效性和交易得到B的授权。比特币于众不同的一点是,B的公钥在B发起交易之前是不公开的。 在比特币系统中,比特币通过区块链上的一笔笔交易在不同的地址间传递。区块链上的每一笔交易都能被验证以确保比特币交易的有效性。比特币交易的相互连接

比特币脚本语言

你可能会以为仅仅通过在交易内容中附上签名就可以签署比特币交易,其实不然,这个过程十分复杂。实际上,每一笔交易中都包含一个“小程序”,用于确认交易是否有效。这个“小程序”用脚本语言写成,通过这种基于堆栈的比特币脚本语言,我们可以应对许多复杂的比特币支付场景。例如,托管系统可以设定只要经过三分之二的用户授权,就可执行交易的规则,也可以设置其他的合约。 脚本语言十分复杂,大约有80种操作码,包括算数计算,按位操作,字符串处理,条件语句和堆栈操作。脚本语言也包含一些必要的密码学操作(SHA-256,RIPEMD等等)作为原语(原语是执行过程中不可被打断的基本操作,你可以理解为一段代码)。为了确保脚本语言可以运行完毕自动退出,该语言不支持任何循环操作,因此它不是图灵完备的。然而,实际上,它只支持少数类型的交易。 前一个交易中的脚本称为scriptPubKey,当前交易中的脚本称为scriptSig。要验证交易时,先执行scriptSig,然后再执行scriptPubKey。如果两个脚本都成功执行,交易就被认定为有效,交易中的比特币就可以成功花出。否则,交易无效。要注意的是前一个交易中的scriptPubKey规定了花费比特币的条件,当前交易的scriptSig必须满足这个条件。 在一个标准的交易中,scriptSig脚本将从私钥中生成的签名并压入堆栈中,再压入公钥。接下来scriptPubKey脚本会执行运算先验证公钥的有效性,再验证签名的有效性。正如脚本中所表示,scriptSig:PUSHDATA

signature data and SIGHASH_ALL

PUSHDATA

public key data复制scriptPubKey:OP_DUP

OP_HASH160

PUSHDATA

Bitcoin address (public key hash)

OP_EQUALVERIFY

OP_CHECKSIG复制当这段代码执行时,PUSHDATA操作首先会把签名压入堆栈,接着把公钥压入堆栈。OPHASH-160操作计算公钥的160位哈希值,PUSHDATA操作再把交易中的输入地址(输入账号)压入堆栈,然后,OP-EQUALVERIFY操作验证验证前两个堆栈中的值是否相等(验证这笔交易中你使用的比特币是否属于你自己)-如果公钥的哈希等于之前交易中的输出地址,这就证明公钥是有效的(证明这个比特币是你的)。最后,OP_CHECKSIG操作将检查交易的签名是否与堆栈里的公钥和签名匹配,匹配就证明签名是有效的(证明交易的到了你的授权)签署交易

我发现签署这笔交易是手动使用比特币时最难的地方,这一过程出奇地困难且容易出错。签名的基本思想很简单,使用椭圆曲线签名算法和私钥来生成交易的数字签名,但细节非常棘手。签署交易的过程可以通过这19个步骤描述。签署交易的19个步骤

对交易的签名让我面临巨大的挑战,这涉及到一个如何在交易内容中还没有加入签名时签署这笔交易的问题。为了避免这个问题,在计算生成签名之前,我把scriptPubKey这个脚本从上一笔交易复制到当前交易中(当前这笔交易正在被签署),然后将签名转换为脚本语言的代码,创建嵌入在当前交易中的scriptSig脚本。对于具有多个输入的交易,签署交易环节更加复杂,因为每个输入都需要单独的签名,这里我就不做详细讨论了。哈希值这一步骤难倒了我。在签名之前,交易中有一个临时附加的哈希值常量。对于常规的交易,这个值是SIGHASH_ALL(0x00000001)。签名后,这个哈希值将从交易内容的最后删除,附加到scriptSig脚本中。在比特币中另一件令人讨厌的事情是虽然签名和公钥都是512位的椭圆曲线值,但它们的表示方式完全不同:签名用DER编码方式编码,而公钥用纯字节表示。另外,两个值都有一个额外的字节,但位置并不一致:SIGHASH_ALL这个附加的哈希值常量放在签名后面,而04这个值放在公钥前面。由于ECDSA算法需要使用随机数,所以调试签名十分困难。每次计算出的签名都会有所不同,因此无法与已知正确的签名进行比较。正是由于上述的复杂性,我花了很长时间才得到了一个签名。不过,最终我找出了签名代码中所有的错误,并成功用它签署了一笔交易。这是我使用的签名代码:def makeSignedTransaction(privateKey, outputTransactionHash, sourceIndex, scriptPubKey, outputs):

myTxn_forSig = (makeRawTransaction(outputTransactionHash, sourceIndex, scriptPubKey, outputs)

+ "01000000") # hash code

s256 = hashlib.sha256(hashlib.sha256(myTxn_forSig.decode('hex')).digest()).digest()

sk = ecdsa.SigningKey.from_string(privateKey.decode('hex'), curve=ecdsa.SECP256k1)

sig = sk.sign_digest(s256, sigencode=ecdsa.util.sigencode_der) + '\01' # 01 is hashtype

pubKey = keyUtils.privateKeyToPublicKey(privateKey)

scriptSig = utils.varstr(sig).encode('hex') + utils.varstr(pubKey.decode('hex')).encode('hex')

signed_txn = makeRawTransaction(outputTransactionHash, sourceIndex, scriptSig, outputs)

verifyTxnSignature(signed_txn)

return signed2_txn复制txnUtils.py最终的scriptSig脚本中包含签名以及比特币源地址的公钥(1MMMMSUb1piy2ufrSguNUdFmAcvqrQF8M5)。 这证明这笔交易有效,我可以花费这些比特币。PUSHDATA 4747signature(DER)sequence30length44integer02length20X2c b2 65 bf 10 70 7b f4 93 46 c3 51 5d d3 d1 6f c4 54 61 8c 58 ec 0a 0f f4 48 a6 76 c5 4f f7 13integer02length20Y6c 66 24 d7 62 a1 fc ef 46 18 28 4e ad 8f 08 67 8a c0 5b 13 c8 42 35 f1 65 4e 6a d1 68 23 3e 82SIGHASH_ALL01PUSHDATA 4141public keytype04X14 e3 01 b2 32 8f 17 44 2c 0b 83 10 d7 87 bf 3d 8a 40 4c fb d0 70 4f 13 5b 6a d4 b2 d3 ee 75 13Y10 f9 81 92 6e 53 a6 e8 c3 9b d7 d3 fe fd 57 6c 54 3c ce 49 3c ba c0 63 88 f2 65 1d 1a ac bf cd最终的scriptPubKey脚本包含成功花费比特币时必须执行的脚本。需要注意的是,这个脚本将在未来花费这些比特币的时候执行。它包含以十六进制表示而不是以Base58Check表示的目标地址1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa,脚本的效果是只有这个目标地址的私钥所有者才能使用比特币,因此目标地址实际上是这些比特币的所有者OP_DUP76OP_HASH160a9PUSHDATA 1414public key hashc8 e9 09 96 c7 c6 08 0e e0 62 84 60 0c 68 4e d9 04 d1 4c 5cOP_EQUALVERIFY88OP_CHECKSIGac最终的交易

经过上述的一系列操作,我们完成了最终的交易。但是,别忘了,此时的交易还没加入区块链中,接收方还没有收到你的比特币。privateKey = keyUtils.wifToPrivateKey("5HusYj2b2x4nroApgfvaSfKYZhRbKFH41bVyPooymbC6KfgSXdD") #1MMMM

signed_txn = txnUtils.makeSignedTransaction(privateKey,

"81b4c832d70cb56ff957589752eb4125a4cab78a25a8fc52d6a09e5bd4404d48", # output (prev) transaction hash

0, # sourceIndex

keyUtils.addrHashToScriptPubKey("1MMMMSUb1piy2ufrSguNUdFmAcvqrQF8M5"),

[[91234, #satoshis

keyUtils.addrHashToScriptPubKey("1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa")]]

)

txnUtils.verifyTxnSignature(signed_txn)

print'SIGNED TXN', signed_txn复制makeTransaction.py最终的交易信息如下所示:version01 00 00 00input count01inputprevious output hash(reversed)48 4d 40 d4 5b 9e a0 d6 52 fc a8 25 8a b7 ca a4 25 41 eb 52 97 58 57 f9 6f b5 0c d7 32 c8 b4 81previous output index00 00 00 00script length8ascriptSig47 30 44 02 20 2c b2 65 bf 10 70 7b f4 93 46 c3 51 5d d3 d1 6f c4 54 61 8c 58 ec 0a 0f f4 48 a6 76 c5 4f f7 13 02 20 6c 66 24 d7 62 a1 fc ef 46 18 28 4e ad 8f 08 67 8a c0 5b 13 c8 42 35 f1 65 4e 6a d1 68 23 3e 82 01 41 04 14 e3 01 b2 32 8f 17 44 2c 0b 83 10 d7 87 bf 3d 8a 40 4c fb d0 70 4f 13 5b 6a d4 b2 d3 ee 75 13 10 f9 81 92 6e 53 a6 e8 c3 9b d7 d3 fe fd 57 6c 54 3c ce 49 3c ba c0 63 88 f2 65 1d 1a ac bf cdsequenceff ff ff ffoutput count01outputvalue62 64 01 00 00 00 00 00script length19scriptPubKey76 a9 14 c8 e9 09 96 c7 c6 08 0e e0 62 84 60 0c 68 4e d9 04 d1 4c 5c 88 acblock lock time00 00 00 00小插曲:椭圆曲线签名

比特币的签名算法使用到了椭圆曲线签名算法,这么实用的功能,你可能会好奇它是怎么做到的?在当年英国数学家安德鲁·怀尔斯攻克费马大定理时,我第一次接触到了椭圆曲线的算法。椭圆曲线的数学思想很有意思,所以在这里我给大家做一个快速的概述。椭圆曲线这个叫法令人迷惑,因为椭圆曲线并不是椭圆,而且看起来也不像椭圆,甚至椭圆曲线与椭圆相关性都很少。通俗地讲,椭圆曲线就是满足一个简单方程y ^ 2 = x ^ 3 + ax + b的曲线。比特币中使用的称为secp256k1的椭圆曲线,它满足的方程为y ^ 2 = x ^ 3 + 7。secp256k1椭圆曲线椭圆曲线的一个重要特性就是你可以用一个简单的规则来定义椭圆曲线上点的相加:如果在曲线上绘制一条直线,这条直线与曲线交与A,B,C三个点,那么这个加法定义为A+B+C=0。由这个加法的定义,我们可以定义整数乘法:例如4A = A + A + A + A。为什么椭圆曲线在密码学上很有用?因为椭圆曲线做整数乘法运算速度很快,但做除法时需要蛮力。例如,你可以快速地计算一个乘法12345678*A = Q,但是如果你只知道A和Q,求解n*A=Q中的n十分困难。因此在椭圆曲线算法中,这里的12345678将是私钥,曲线上的点Q将是公钥。在密码学中,点的坐标并不是它在曲线上的实值点,而是对整数的模数。椭圆曲线的一个好用的特性就是对实数或模数进行运算的数学运算几乎相同。正因为如此,比特币的椭圆曲线并不像上面的图片,而是一团杂乱无章的256位点集(想想在一个空间中充满了大量杂乱无章的点)。椭圆曲线数字签名算法(ECDSA)接收交易的哈希值,使用该交易数据,私钥,以及一个随机数从椭圆曲线上生成一个新的点,从而实现对交易的签名。任何拥有公钥,交易数据,和签名的人都可以通过做一个简单的椭圆曲线运算来验证签名的有效性。读到这里,你应该明白了为什么只有拥有私钥的人才能签署消息,但拥有公钥的任何人都可以验证该消息。把交易发送到比特币网络

回到交易中来,别忘了此时我们的交易还没有被加入到区块链中,还不是一笔有效交易。刚刚我创建并签署了一笔交易。下一步就是将这笔交易发送到比特币网络中,网络中的矿工会收集交易并把它打包进区块中。如何找到比特币网络的节点

首先我要在比特币的点对点网络中找到一个节点。节点的列表会随节点的进出动态更新,当一个比特币节点连接到另一个节点时,它们就会不断交换彼此新发现的比特币节点信息,因此,新节点加入的消息会快速地传遍整个网络。然而,新的比特币节点如何第一次找到比特币节点?这是一个先有鸡还是先有蛋的问题。比特币节点通过以下几种方法来解决这个问题。有几个可信的比特币节点会以bitseed.xf2.org的域名在DNS系统(Domain Name System,域名系统,万维网上作为域名和IP地址相互映射的一个分布式数据库)上注册,通过执行nslookup命令,你就可以得到这些节点的IP地址,只要有一个在工作即可。如果很不幸它们都没有工作的话,你可以试着连接那几个已经在你的客户端中硬编码记录下来的地址。Nslookup命令可以用来寻找比特币节点

当用户启动或停止比特币客户端时,节点就会加入或离开比特币网络。所以连接节点有很大的不确定性,在我实验时,就遇到了连接的节点已经离开比特币网络的情况,如果你想重复我的实验,最好多找几个节点,可能需要多次尝试才能找到一个运行着的节点。与比特币节点通信

一旦获得了一个正在工作的比特币节点的IP地址,当务之急就通过这个节点是把我的交易发送到比特币的点对点网络中。使用点对点的网络协议十分简单,我在端口8333上打开了一个到任意对等端的TCP连接,发送消息,然后接受反馈消息。比特币的点对点协议对用户很友好,即使我的请求数据出错时,还是继续与我保持通信。 重要提示:正如一些人指出的那样,如果你想重复我的实验,切记要使用比特币的测试网络,在测试网络上,你可以使用“虚拟”的比特币来进行交易。因为在真实网络上,万一你不小心,有可能会失去所有的比特币。还记得上面提到的那个100个比特币转账1个的交易么,如果你忘了将剩余的比特币转给自己,那么剩余的99个比特币就会作为交易费支付给矿工。但是本着科学的态度,我并不在意在真实的比特币网络中损失我这些价值1美元的比特币。协议中包含24种不同的信息种类。每一条信息都是一个简单的二进制大对象(binary large object ,BLOB,是一个可以存储二进制文件的容器),其中包含一个ASCII命令和一个适用该命令的二进制有效参数。该协议可以在比特币的维基上查询。 连接到比特币网络的第一步就是通过交换客户端版本信息来建立连接。首先,我发送了一条客户端版本信息,其中包含我的协议版本号,IP地址和其他内容。比特币节点也向我回复了它的版本信息。在此之后,我应该回复一个verack信息(version acknowledgement,版本确认)来确认它的版本信息。正如我所说,比特币点对点网络协议对用户十分友好,即使我跳过了verack信息,之后的操作也是一切正常。 交换版本信息这一步并不简单,因为信息具有标准的格式,不过不用害怕,可以用几行代码来创建这些信息。下面代码段中的makeMessage函数可以由随机数,命令名以及命令的参数来生成一条消息。getVersionMessage函数通过将各个字段打包在一起来为版本消息创建参数。magic = 0xd9b4bef9

def makeMessage(magic, command, payload):

checksum =

hashlib.sha256(hashlib.sha256(payload).digest()).digest()[0:4]

return struct.pack('L12sL4s', magic, command, len(payload), checksum) + payload

def getVersionMsg():

version = 60002

services = 1

timestamp = int(time.time())

addr_me = utils.netaddr(socket.inet_aton("127.0.0.1"), 8333)

addr_you = utils.netaddr(socket.inet_aton("127.0.0.1"), 8333)

nonce = random.getrandbits(64)

sub_version_num = utils.varstr('')

start_height = 0

payload = struct.pack('

addr_you, nonce, sub_version_num, start_height)

return makeMessage(magic, 'version', payload)复制msgUtils.py发送交易tx

我使用下面精简的Python代码把我的交易发送到比特币网络中,这个代码发送一条客户端版本信息,接受(也可以忽略)比特币节点的版本信息和verack信息。最后将我的交易以tx信息发送。代码中这个16进制的字符串是我之前创建的交易。def getTxMsg(payload): return makeMessage(magic, 'tx', payload)

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

sock.connect(("97.88.151.164", 8333))

sock.send(msgUtils.getVersionMsg())

sock.recv(1000) # receive version

sock.recv(1000) # receive verack

sock.send(msgUtils.getTxMsg("0100000001484d40d45b9ea0d652fca8258ab7caa42541eb52975857f96fb50cd732c8b481000000008a47304402202cb265bf10707bf49346c3515dd3d16fc454618c58ec0a0ff448a676c54ff71302206c6624d762a1fcef4618284ead8f08678ac05b13c84235f1654e6ad168233e8201410414e301b2328f17442c0b8310d787bf3d8a404cfbd0704f135b6ad4b2d3ee751310f981926e53a6e8c39bd7d3fefd576c543cce493cbac06388f2651d1aacbfcdffffffff0162640100000000001976a914c8e90996c7c6080ee06284600c684ed904d14c5c88ac00000000".decode('hex')))复制minimalSendTxn.py以下Wireshark(一个抓取,分析网络封包的软件)软件的截图显示出我是如何将交易发送到比特币网络中的。我用Python编写了脚本来分析网络数据,为了简单起见,在这里我使用Wireshark。从图中可以看到我的这笔tx交易。Wireshark中抓取的这笔正在上传至比特币网络的交易tx为了实时监控我这笔交易的进度,我在比特币网络中新运行了一个节点,在把我交易发到比特币网络5秒钟之后,另一个节点给我发送了这个tx消息,其中包含我刚刚发送的这笔交易的哈希,由此可见,在仅仅这几秒中,我的交易已经传遍了比特币网络,至少也是比特币网络的一部分。交易成功:我的交易被加入区块链

在将我的交易发送比特币网络之后,我需要等待它被矿工开采出来加入到区块链中,然后才能宣称我的实验圆满成功。10分钟后,我的比特币节点收到一条含有新区块信息的inv消息(参见下图Wireshark抓到的网络封包),检查这个区块后发现我的交易被包含在了区块中,证明我的交易是有效的,我的实验成功了。通过我的比特币钱包软件和在线查询,再一次确认了我已经交易成功。可以说,经过不断的努力,我成功手动创建了一笔交易,并让比特币系统接受了它。(当然了,我也经过了几次失败的尝试,这些错误的交易都消失在了网络之中,永远都不会被检索到。Wireshark中抓取的新区块产生的封包信息

我的交易是被当时哈希算力(挖矿速度)最大的矿池(多个矿工一起挖矿)GHash.IO挖出,区块高度为279068,区块哈希为0000000000000001a27b1d6eb8c405410398ece796e742da3b3e35363c2219ee,在上图Wireshark数据包中inv消息的哈希值是经前后反转得到的ee192……。你应该会发现区块的哈希值以大量的0开头,在一个16进制的哈希值中发现一个以这么多0开头的数,这就是为什么挖矿如此困难的原因。这个区块中由462笔交易,我的交易是其中之一。高度为279068的区块以及我发起的这笔交易

(https://blockchain.info/block-index/341440/0000000000000001a27b1d6eb8c405410398ece796e742da3b3e35363c2219ee)挖到这个区块的矿工们收到了25个比特币的奖励,交易费总共是0.104个比特币,按当时的市价分别为19000美元和80美元。我支付了0.0001个比特币的交易费,大约是我交易额的10%,按当时的市价为8美分。结论

手动进行比特币交易比我想象中困难得多,但是在这个过程中我学到了很多,希望你也是。我的Python代码仅仅是为了介绍,如果你想跟我一样用Python手动进行比特币交易,也可以试试这几个项目。https://en.bitcoin.it/wiki/Bitcoin-pythonhttps://github.com/richardkiss/pycoinhttps://github.com/jgarzik/python-bitcoinlib写在最后2017年是区块链的井喷之年,经过一年的积攒,2018年将迎来区块链的落地之年,区块链会逐渐颠覆各行各业。对于个人,区块链的机会会越来越多,也许你错过了比特币的投资,不妨现在抓住区块链这个风口,投资自己,多学习相关知识,区块链大有可为,投身区块链的你将大有作为!顺便打个广告,链接如下,感兴趣的同学不妨点进去看看。孟岩领衔研发PDJ区块链全阶实战课程二期班正式面市https://huiyi.csdn.net/activity/product/goods_list?project_id=3804大家可以比较市面上的不同类型区块链课程,货比三家,选适合自己的,赶紧上车是王道。本文参与 腾讯云自媒体分享计划,分享自微信公众号。原始发表:2018-04-02,如有侵权请联系 cloudcommunity@tencent.com 删除python比特币区块链本文分享自 区块链大本营 微信公众号,前往查看如有侵权,请联系 cloudcommunity@tencent.com 删除。本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!python比特币区块链评论登录后参与评论0 条评论热度最新登录 后参与评论推荐阅读LV.关注文章0获赞0目录比特币简述:

比特币交易

点对点网络

加密技术

比特币协议探究

比特币地址和密钥

从内部分析一笔交易

手动创建一笔交易

比特币交易怎样签署

比特币脚本语言

签署交易

最终的交易

小插曲:椭圆曲线签名

把交易发送到比特币网络

如何找到比特币网络的节点

与比特币节点通信

发送交易tx

交易成功:我的交易被加入区块链

结论

写在最后相关产品与服务区块链云链聚未来,协同无边界。腾讯云区块链作为中国领先的区块链服务平台和技术提供商,致力于构建技术、数据、价值、产业互联互通的区块链基础设施,引领区块链底层技术及行业应用创新,助力传统产业转型升级,推动实体经济与数字经济深度融合。产品介绍2024新春采购节领券社区专栏文章阅读清单互动问答技术沙龙技术视频团队主页腾讯云TI平台活动自媒体分享计划邀请作者入驻自荐上首页技术竞赛资源技术周刊社区标签开发者手册开发者实验室关于社区规范免责声明联系我们友情链接腾讯云开发者扫码关注腾讯云开发者领取腾讯云代金券热门产品域名注册云服务器区块链服务消息队列网络加速云数据库域名解析云存储视频直播热门推荐人脸识别腾讯会议企业云CDN加速视频通话图像分析MySQL 数据库SSL 证书语音识别更多推荐数据安全负载均衡短信文字识别云点播商标注册小程序开发网站监控数据迁移Copyright © 2013 - 2024 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有 深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569腾讯云计算(北京)有限责任公司 京ICP证150476号 |  京ICP备11018762号 | 京公网安备号11010802020287问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档Copyright © 2013 - 2024 Tencent Cloud.All Rights Reserved. 腾讯云 版权所有登录 后参与评论00

python程序实现BTC(比特币)挖矿的完整代码 / 张生荣

python程序实现BTC(比特币)挖矿的完整代码 / 张生荣

javascript

java

python

Android

IOS

php

python程序实现BTC(比特币)挖矿的完整代码

2024-02-09 00:08:15

区块链的作业是比特币挖矿程序python实现,我做完了跟大家分享一波。

完整代码如下:

#Author:Mr.Pan_学狂

#Finish_time:2020/11/22/22:34

import hashlib

import time

def find_gold():#参数D是难度系数

x = 0#算力初始值

Diffcult_number = str(input('请输入难度系数:'))

print('开始挖矿!!')

while True:

x += 1

string = 'hello'

start_string = str(x)

combine_string = start_string + string#字符串拼接

h = hashlib.sha256()#采用sha256加密

h.update(combine_string.encode('utf-8'))#utf-8编码后哈希加密

gold_hash = h.hexdigest()

#print(gold_hash)

startTime = time.perf_counter()

if gold_hash[0:len(Diffcult_number)] == Diffcult_number:#对比哈希值的x位与难度系数是否相同,x为难度系数

print('算力:{}'.format(x), '用时:{}'.format(startTime))

print('哈希值:%s' % gold_hash)#占位符

print('挖矿成功!!')

break

elif gold_hash[0:len(Diffcult_number)] != Diffcult_number:#哈希值前x位与难度系数不相同

print('算力:{}'.format(x),'用时:{}'.format(startTime))

print('哈希值:%s' % gold_hash)

print('没有挖到!!')

print()

continue

if __name__ == '__main__':#主函数,调用函数,程序出口。

find_gold()

我设置的难度规则系数是:‘0abd',若哈希值的最前面四位是'0abd'则显示挖矿成功!!否则,挖矿失败。经过一段时间后的运行结果,如下图:

我觉得随着时代的进步,矿机的升级,个人PC机(服务器)想要挖到比特币是很难的。并且,目前出现了专业的挖矿公司和挖矿团队,个人的算力就根本微不足道了。所以,现在就是租赁挖矿公司的服务器,根据挖到的成果分红。还有一个问题,就是环保问题,当矿机工作的时候会产生噪声,而且特别消耗电力,会排放大量的二氧化碳。我个人认为仅仅只是为了获得记账权的服务费,而去挖矿其实不是太理想,因为浪费了大量的资源,包括计算机的算力,能源等等。

到此这篇关于python程序实现BTC(比特币)挖矿的完整代码的文章就介绍到这了,更多相关python实现BTC(比特币)挖矿内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

赞 (0)

相关推荐

普通人如何在区块链行业赚钱

区块链的世界里,没有新鲜事. 技术是技术,通证(token,以前被翻译为"代币")是通证.通证是区块链网络的必然产物.代表着背后的技术应用,落地方向,潜在价值.也被称为"数字资产". 如何在区块链行业赚钱?我认真分析整理了一下币圈链圈的信息,总结如下: 1:通过挖矿等方式获得更多数量的数字资产.可以是"挖矿",也可以是"私募"兑换,也可以是"ic-o".形式大致就这么几种,目的是为了让你手里的数字资产的数量

C++实现区块链的源码

看了上面的算法,相信大家基本可以猜到,相对于比特币的限量的性质,对于本算法来说,难解程度的根本原因即为向量环路的迭代次数.迭代次数越多,则算法越难解,从而导致解题需要花费更多的时候,再基于这点,在数学上,当解题次数足够大时,效率会无限小,从而导致了解题时间无限长最后导致加密货币的发放无限小. 创世区块创建(部分大妈在前面有实现,而区块这一部分将会详细解答) void Make_First_Block() { Getpublickey(); blo.data = circle; blo.pre_h

JavaScript实现区块链

几乎每个人都听说过像比特币和以太币这样的加密货币,但是只有极少数人懂得隐藏在它们背后的技术.在这篇文中,我将会用JavaScript来创建一个简单的区块链来演示它们的内部究竟是如何工作的.我将会称之为SavjeeCoin! 全文分为三个部分: part1:实现一个基本的区块链 part2:实现POW part3:交易与挖矿奖励 Part1:实现一个基本的区块链 区块链 区块链是由一个个任何人都可以访问的区块构成的公共数据库.这好像没什么特别的,不过它们有一个有趣的属性:它们是不可变的.一旦一个区

Go语言开发区块链只需180行代码(推荐)

区块链开发用什么语言?通过本文你将使用Go语言开发自己的区块链(或者说用go语言搭建区块链).理解哈希函数是如何保持区块链的完整性.掌握如何用Go语言编程创造并添加新的块.实现多个节点通过竞争生成块.通过浏览器来查看整个链.了解所有其他关于区块链的基础知识. 但是,文章中将不会涉及工作量证明算法(PoW)以及权益证明算法(PoS)这类的共识算法,同时为了让你更清楚得查看区块链以及块的添加,我们将网络交互的过程简化了,关于 P2P 网络比如"全网广播"这个过程等内容将在后续文章中补上.

Python实现类似比特币的加密货币区块链的创建与交易实例

虽然有些人认为区块链是一个早晚会出现问题的解决方案,但是毫无疑问,这个创新技术是一个计算机技术上的奇迹.那么,究竟什么是区块链呢? 区块链 以比特币(Bitcoin)或其它加密货币按时间顺序公开地记录交易的数字账本. 更通俗的说,它是一个公开的数据库,新的数据存储在被称之为区块(block)的容器中,并被添加到一个不可变的链(chain)中(因此被称为区块链(blockchain)),之前添加的数据也在该链中.对于比特币或其它加密货币来说,这些数据就是一组组交易,不过,也可以是其它任何类型的数据

C++实现比特币系统的源码

这是我最近写的一个近乎于完整的模拟比特币的代码,实际上还不够完整,不过已经能实现打包交易进入区块,然后挖矿了.这个程序是只有一个节点在挖矿,所以暂时也不涉及分布式系统的东西,还算比较简单容易理解.这个程序就是整个网络中一个节点的视角.分享出来希望对大家有所帮助. 部署过程就是把代码统统贴过去,要注意的就是代码中有两个文件的路径,要手动改一下,代码中有两个文件的路径,要手动改一下,代码中有两个文件的路径,要手动改一下,重要的事情说三遍,然后就可以运行,观察比特币挖矿过程啦!有问题欢迎提问. Blo

使用go实现简易比特币区块链公链功能

使用go语言实现具备以下功能的简易区块链 区块与区块链 共识机制 数据库 Cli命令行操作 交易管理 密码学 数字签名 交易缓存池 P2P网络管理 由于平时还要进行论文工作,项目不定时更新 2021.1.1实现了区块结构.区块链结构.工作量证明pow,剩下部分陆续更新 1.实现区块结构 package BLC import ( "bytes" "crypto/sha256" "time" ) //实现一个最基本的区块结构 type Block s

比特币上的数独游戏合约的实现代码

我们在 Bitcoin SV 上实现了一个数独游戏智能合约,利用之前介绍过的一种合约范式可以将游戏中寻找解题方案的过程外包上链.因为求解数独问题的计算工作量会随着其行列数快速增长,实际上它也是一个 NP-完全 问题.不过我们可以借由比特币智能合约巧妙地寻求答案,只需要验证答案提供者所给出的解答是否满足要求即可,这样即可以将复杂的求解计算过程实现链下外包. sCrypt 合约代码如下: import "util.scrypt"; import "array.scrypt&quo

python程序实现BTC(比特币)挖矿的完整代码

区块链的作业是比特币挖矿程序python实现,我做完了跟大家分享一波. 完整代码如下: #Author:Mr.Pan_学狂 #Finish_time:2020/11/22/22:34 import hashlib import time def find_gold():#参数D是难度系数 x = 0#算力初始值 Diffcult_number = str(input('请输入难度系数:')) print('开始挖矿!!') while True: x += 1 string = 'hello'

Python实现简单网页图片抓取完整代码实例

利用python抓取网络图片的步骤是: 1.根据给定的网址获取网页源代码 2.利用正则表达式把源代码中的图片地址过滤出来 3.根据过滤出来的图片地址下载网络图片 以下是比较简单的一个抓取某一个百度贴吧网页的图片的实现: # -*- coding: utf-8 -*- # feimengjuan import re import urllib import urllib2 #抓取网页图片 #根据给定的网址来获取网页详细信息,得到的html就是网页的源代码 def getHtml(url): pag

微信小程序支付功能 php后台对接完整代码分享

微信小程序支付,php后台对接完整代码,全是干货呀,拿过来可以直接使用.小程序在调起微信支付之前需要5个参数,这时候就需要携带code向后台请求,然后后台根据code获取openid 再进行服务器之间的. 一.准备工作 1.小程序注册,要以公司的以身份去注册一个小程序,才有微信支付权限: 2.绑定商户号. 3.在小程序填写合法域  二.完成以上条件,你可以得到      小程序appid 小程序秘钥    这两个用于获取用户openid: 商户号id ,商户号秘钥     支付接口必须的: 三.

Python统计词频并绘制图片(附完整代码)

效果 1 实现代码 读取txt文件: def readText(text_file_path): with open(text_file_path, encoding='gbk') as f: # content = f.read() return content 得到文章的词频: def getRecommondArticleKeyword(text_content, key_word_need_num = 10, custom_words = [], stop_words =[], quer

python opencv 画外接矩形框的完整代码

画外接矩形框,可以画成一个最大的,也可以分别画. # -*- coding: utf-8 -*- import cv2 image = cv2.imread('G:/110w2/mask_tif4/00.png') print(image.shape) print(image.shape[0]) # h print(image.shape[1]) # w # 图像转灰度图 img = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) #cv2.imwrite('G:

用python画个奥运五环(附完整代码)

完整代码 #绘制奥运五环 import turtle #导入turtle包 turtle.width(15) #定义线条宽度为15 turtle.color("red") turtle.circle(50) turtle.penup() #定义颜色,园半径,抬笔 turtle.goto(120,0) turtle.pendown() #定义线条走向,落笔 turtle.color("green") turtle.circle(50) turtle.penup() t

用python写一个福字(附完整代码)

目录 前言: 一,扫五福活动如此火爆,为何不自己利用编程来生成福字! 二,完整代码 三,总结 前言: 支付宝 2022 集五福活动正式开启 数据显示,过去六年累计参与支付宝集五福的人数已经超过了 7 亿,每 2 个中国人里就有 1 个曾扫福.集福.送福. 一,扫五福活动如此火爆,为何不自己利用编程来生成福字! 首先作品奉上: ①,导入python库 import io from PIL import Image import requests ②,利用爬虫,获取单个汉字 def get_word

python实现图片转字符画的完整代码

前言 最初是在实验楼看到的一个小实验 实验楼-Python 图片转字符画 原文是需要通过命令行运行程序 这里改为直接运行,需要固定一些参数 运行平台: Windows Python版本: Python3.6 IDE: Sublime Text 1.实验准备 pillow库的安装 pip install pillow 2.实验原理 字符画是一系列字符的组合,我们可以把字符看作是比较大块的像素,一个字符能表现一种颜色,字符的种类越多,可以表现的颜色也越多,图片也会更有层次感. 最终显示的是黑白色的字

使用 prometheus python 库编写自定义指标的方法(完整代码)

虽然 prometheus 已有大量可直接使用的 exporter 可供使用,以满足收集不同的监控指标的需要.例如,node exporter可以收集机器 cpu,内存等指标,cadvisor可以收集容器指标.然而,如果需要收集一些定制化的指标,还是需要我们编写自定义的指标. 本文讲述如何使用 prometheus python 客户端库和 flask 编写 prometheus 自定义指标. 安装依赖库 我们的程序依赖于flask和prometheus client两个库,其 requirem

python之pygame模块实现飞机大战完整代码

本文实例为大家分享了python之pygame模块实现飞机大战的具体代码,供大家参考,具体内容如下 Python飞机大战步骤: 1.数据区 2.主界面 3.飞船 4.事件监控及边界 5.外星人 6.记分系统 飞机大战效果图: 源码: """ 功能:飞机大战 time:2019/10/3 """ import os import pygame import sys import time from pygame.sprite import Spri

随机推荐

原生JavaScript实现异步多文件上传

收集的ROS防火墙脚本

浅谈android中数据库的拷贝

jvm内存溢出解决方法(jvm内存溢出怎么解决)

Oracle数据库并行查询出错的解决方法

用javascript获取地址栏参数

ASP.NET下使用xml反序列化、缓存依赖实现个性化配置文件的实时生效

php redis实现文章发布系统(用户投票系统)

Python def函数的定义、使用及参数传递实现代码

Android获取SD卡路径及SDCard内存的方法

使用JS批量选中功能实现更改数据库中的status状态值(批量展示)

复习一下sql server的差异备份

比Jquery的document.ready更快的方法

Android树形控件绘制方法

SEO优化必备.各大搜索引擎关键字数据参考网址

.Net页面局部更新引发的思考

Android使用Intent实现页面跳转

android获取屏幕的长与宽实现代码(手写)

iOS新功能引导提示界面实例详解

Cisco路由技术基础知识详解之一

技术开发教程技术博客开发博客

© 2024 张生荣

15 行Python代码实现比特币挖矿 全面Python教程 - 视频总结 - Glarity

Python代码实现比特币挖矿 全面Python教程 - 视频总结 - GlarityThis page requires JavaScript to be enabled for full functionality.· 视频总结15 行Python代码实现比特币挖矿 全面Python教程本文是一篇YouTube视频的摘要"15 行Python代码实现比特币挖矿 全面Python教程"视频创作者 :Pyresearch一句话总结视频:这段视频教程介绍了如何使用 15 行 Python 代码进行比特币挖矿,主要讲解了哈希算法的运行过程以及如何进行简单的比特币挖矿。#Bitcoin​为任何视频生成总结和文字转录 添加至 Chrome免费无广告视频亮点 [] 视频介绍了哈希算法的基本概念和安全哈希算法的作用。 [⛏️] 使用 Python 编写了简单的比特币挖矿算法,通过对哈希值进行编码和解码实现挖矿过程。 [⏱️] 通过测量时间来评估挖矿过程的效率,展示了挖矿所需的时间和交易 ID。 [‍] 提供了在 GitHub 上的代码链接以及安装哈希库的方法。 [] 强调了在代码中进行调整以适应个人系统性能的重要性,同时提供了交易次数的设置建议。 [] 鼓励观众喜欢视频并提供反馈意见。更多视频关于 科学技术简短摘要在这个YouTube视频中,作者展示了如何使用Python实时跟踪400多种加密货币的价格,并将数据存储在CSV文件中。视频分为两个部分:第一部分是通过Binance的Websocket API获取实时价格数据,并将其存储在CSV文件中;第二部分是从CSV文件中提取数据,进行分析,找出当前上升或下降的加密货币。作者强调了这种方法的简便性和高效性,以及观众可以通过观看整个视频了解整个加密货币市场的实时情况。深入阅读>使用 Python 实时提取和分析超过 450 种硬币 [超级简单且高效]简短摘要这是一个关于使用Python进行算法交易的免费教程,由Free Code Camp提供。课程作者是Nick McCollum,他将教授如何构建三个量化金融项目。这三个项目分别是:构建一个等权重版本的标普500指数基金、构建一个基于动量投资指标的量化策略、以及构建一个基于价值指标的量化策略。深入阅读>使用 Python 进行算法交易 - 完整课程简短摘要这篇内容介绍了2024年比特币挖矿新人教学,讲解了比特币的发展趋势、挖矿成本、比特币云算力挖矿项目Bitroo的详细介绍以及套期保值的方法。深入阅读>2024年比特币挖矿教程-比特币云算力挖矿成本及套期保值方法详解简短摘要这是一个YouTube视频,主题为使用Python和Binance进行自动化交易的介绍,包括使用真实货币进行加密货币交易。视频详细介绍了如何设置和使用自动化交易系统,包括安装Python包,理解和处理数据,创建和执行交易订单等步骤。深入阅读>Python和Binance自动交易实战教程 - 加密货币真实交易简短摘要本视频内容主要涵盖了区块链基础、交易、未花费交易输出(UTXO)以及比特币的脚本代码。教授介绍了比特币交易的基本格式,包括输入和输出,以及交易验证的方法。同时,视频提到了比特币的coinbase交易,作为矿工的奖励。教授还分享了关于UTXO的数据库,其中包含未被花费的交易输出。视频中还强调了比特币交易的去中心化特性和交易费的市场机制。总体而言,内容涉及了比特币的设计要素以及相关的经济和技术考虑。深入阅读>比特币基础教程:交易、UTXO、脚本代码详解简短摘要这个视频介绍了如何使用普通电脑挖比特币,以及挖出比特币后是提现还是等待升值。视频推荐使用NiceHash作为挖矿平台,并提供了收益计算器来估算挖矿收益。根据计算器的结果,使用一张RTX 2080显卡可以每月获得约1193元的收益。深入阅读>挖比特币月入1000美金!普通电脑挖矿攻略与提现全解析询问这个视频中的任何问题 添加至 Chrome免费无广告此视频常见问答什么是哈希?

哈希是一种安全的哈希算法,用于加密数据。视频中提到了官方文档哈希的网站以及如何在Python中运行哈希算法。

在Python中如何安装哈希库?

使用以下命令进行安装:`pip install hash256`。该库可以在Python 3.5及以上版本中使用。

如何开始进行比特币挖矿?

首先,在Python中运行挖矿命令 `python bitcoin_mining.py`。然后,通过调用挖矿函数和虚拟交易函数来模拟比特币挖矿过程。视频中提到了如何调用这些函数以及检查挖矿所需的时间。

在比特币挖矿过程中,如何控制挖矿时间?

您可以通过在代码中设置迭代次数来控制挖矿时间。迭代次数越多,挖矿所需的时间就越长。视频中建议根据系统性能来确定迭代次数,通常可以尝试1000次迭代。为任何视频生成总结和文字转录 添加至 Chrome免费无广告0|15 行Python代码实现比特币挖矿 全面Python教程视频创作者 :Pyresearch为任何视频生成总结和文字转录 添加至 Chrome免费无广告0|视频总结→科学技术→15 行Python代码实现比特币挖矿 全面Python教程通过我们的团队获得即时支持加入我们的社区您的跨语言阅读和写作助手关注我们:Glarity DiscordGlarity TwitterGlarity Github资源博客更新日志常见问题定价使用兑换码反馈公司团队隐私政策产品YouTube视频总结PDF总结Twitter总结对照式翻译Gmail快捷回复ChatGPT Plugin免费AI聊天Felo 实时语音翻译需要帮助?还厌倦和AI聊天? 别担心,我们的人工客服来帮助您[email protected]版权所有 © 2024 Sparticle Inc.zh

带你挖矿之旅!Python从零开始创建区块链!提供源码哦!月薪十万-腾讯云开发者社区-腾讯云

之旅!Python从零开始创建区块链!提供源码哦!月薪十万-腾讯云开发者社区-腾讯云诸葛青云带你挖矿之旅!Python从零开始创建区块链!提供源码哦!月薪十万原创关注作者腾讯云开发者社区文档建议反馈控制台首页学习活动专区工具TVP最新优惠活动文章/答案/技术大牛搜索搜索关闭发布登录/注册首页学习活动专区工具TVP最新优惠活动返回腾讯云官网诸葛青云首页学习活动专区工具TVP最新优惠活动返回腾讯云官网社区首页 >专栏 >带你挖矿之旅!Python从零开始创建区块链!提供源码哦!月薪十万带你挖矿之旅!Python从零开始创建区块链!提供源码哦!月薪十万原创诸葛青云关注修改于 2018-09-28 09:40:521.3K0修改于 2018-09-28 09:40:52举报文章被收录于专栏:诸葛青云的专栏诸葛青云的专栏环境准备确保已经安装Python3.6+, pip , Flask, requests,安装方法:pip install Flask==0.12.2 requests==2.18.4同时还需要一个HTTP客户端,比如Postman,cURL或其它客户端。参考https://github.com/xilibi2003/blockchain开始创建Blockchain新建一个文件 blockchain.py,本文所有的代码都写在这一个文件中,可以随时参考https://github.com/xilibi2003/blockchainBlockchain类用来管理链条,它能存储交易,加入新块等,下面我们来进一步完善这些方法。关于怎么快速学python,可以加下小编的python学习群:611+530+101,不管你是小白还是大牛,小编我都欢迎,不定期分享干货

每天晚上20:00都会开直播给大家分享python学习知识和路线方法,群里会不定期更新最新的教程和学习方法,大家都是学习python的,或是转行,或是大学生,还有工作中想提升自己能力的,如果你是正在学习python的小伙伴可以加入学习。最后祝所有程序员都能够走上人生巅峰,让代码将梦想照进现实块结构每个区块包含属性:索引(index),Unix时间戳(timestamp),交易列表(transactions),工作量证明(稍后解释)以及前一个区块的Hash值。以下是一个区块的结构:block = {'index': 1,'timestamp': 1506057125.900785,'transactions': [{'sender': "8527147fe1f5426f9dd545de4b27ee00",'recipient': "a77f5cdfa2934df3954a5c7c7da5df1f",'amount': 5,}],'proof': 324984774000,'previous_hash': "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"}到这里,区块链的概念就清楚了,每个新的区块都包含上一个区块的Hash,这是关键的一点,它保障了区块链不可变性。如果攻击者破坏了前面的某个区块,那么后面所有区块的Hash都会变得不正确。不理解的话,慢慢消化。加入交易接下来我们需要添加一个交易,来完善下new_transaction方法:class Blockchain(object):...def new_transaction(self, sender, recipient, amount):"""生成新交易信息,信息将加入到下一个待挖的区块中:param sender: Address of the Sender:param recipient: Address of the Recipient:param amount: Amount:return: The index of the Block that will hold this transaction"""self.current_transactions.append({'sender': sender,'recipient': recipient,'amount': amount,})return self.last_block['index'] + 1方法向列表中添加一个交易记录,并返回该记录将被添加到的区块(下一个待挖掘的区块)的索引,等下在用户提交交易时会有用。创建新块当Blockchain实例化后,我们需要构造一个创世块(没有前区块的第一个区块),并且给它加上一个工作量证明。每个区块都需要经过工作量证明,俗称挖矿,稍后会继续讲解。为了构造创世块,我们还需要完善new_block(), new_transaction() 和hash() 方法:import hashlibimport jsonfrom time import timeclass Blockchain(object):def __init__(self):self.current_transactions = []self.chain = []# Create the genesis blockself.new_block(previous_hash=1, proof=100)def new_block(self, proof, previous_hash=None):"""生成新块:param proof: The proof given by the Proof of Work algorithm:param previous_hash: (Optional) Hash of previous Block:return: New Block"""block = {'index': len(self.chain) + 1,'timestamp': time(),'transactions': self.current_transactions,'proof': proof,'previous_hash': previous_hash or self.hash(self.chain[-1]),}# Reset the current list of transactionsself.current_transactions = []self.chain.append(block)return blockdef new_transaction(self, sender, recipient, amount):"""生成新交易信息,信息将加入到下一个待挖的区块中:param sender: Address of the Sender:param recipient: Address of the Recipient:param amount: Amount:return: The index of the Block that will hold this transaction"""self.current_transactions.append({'sender': sender,'recipient': recipient,'amount': amount,})return self.last_block['index'] + 1@propertydef last_block(self):return self.chain[-1]@staticmethoddef hash(block):"""生成块的 SHA-256 hash值:param block: Block:return:"""# We must make sure that the Dictionary is Ordered, or we'll have inconsistent hashesblock_string = json.dumps(block, sort_keys=True).encode()return hashlib.sha256(block_string).hexdigest()通过上面的代码和注释可以对区块链有直观的了解,接下来我们看看区块是怎么挖出来的。理解工作量证明新的区块依赖工作量证明算法(PoW)来构造,PoW的目标是找出一个符合特定条件的数字,这个数字很难计算出来,但容易验证。这就是工作量证明的核心思想。为了方便理解,举个例子:假设一个整数 x 乘以另一个整数 y 的积的 Hash 值必须以 0 结尾,即hash(x * y) = ac23dc…0,设变量 x = 5,求 y 的值?用Python实现如下:from hashlib import sha256x = 5y = 0 # y未知while sha256(f'{x*y}'.encode()).hexdigest()[-1] != "0":y += 1print(f'The solution is y = {y}')结果是y=21. 因为:hash(5 * 21) = 1253e9373e...5e3600155e860在比特币中,使用称为Hashcash的工作量证明算法,它和上面的问题很类似。矿工们为了争夺创建区块的权利而争相计算结果。通常,计算难度与目标字符串需要满足的特定字符的数量成正比,矿工算出结果后,会获得比特币奖励。当然,在网络上非常容易验证这个结果。实现工作量证明让我们来实现一个相似PoW算法,规则是:寻找一个数 p,使得它与前一个区块的 proof 拼接成的字符串的 Hash 值以 4 个零开头。import hashlibimport jsonfrom time import timefrom uuid import uuid4class Blockchain(object):...def proof_of_work(self, last_proof):"""简单的工作量证明:- 查找一个 p' 使得 hash(pp') 以4个0开头- p 是上一个块的证明, p' 是当前的证明:param last_proof::return:"""proof = 0while self.valid_proof(last_proof, proof) is False:proof += 1return proof@staticmethoddef valid_proof(last_proof, proof):"""验证证明: 是否hash(last_proof, proof)以4个0开头?:param last_proof: Previous Proof:param proof: Current Proof:return: True if correct, False if not."""guess = f'{last_proof}{proof}'.encode()guess_hash = hashlib.sha256(guess).hexdigest()return guess_hash[:4] == "0000"衡量算法复杂度的办法是修改零开头的个数。使用4个来用于演示,你会发现多一个零都会大大增加计算出结果所需的时间。现在Blockchain类基本已经完成了,接下来使用HTTP requests来进行交互。Blockchain作为API接口我们将使用Python Flask框架,这是一个轻量Web应用框架,它方便将网络请求映射到 Python函数,现在我们来让Blockchain运行在基于Flask web上。我们将创建三个接口:/transactions/new 创建一个交易并添加到区块/mine 告诉服务器去挖掘新的区块/chain 返回整个区块链创建节点我们的Flask服务器将扮演区块链网络中的一个节点。我们先添加一些框架代码:import hashlibimport jsonfrom textwrap import dedentfrom time import timefrom uuid import uuid4from flask import Flaskclass Blockchain(object):...# Instantiate our Nodeapp = Flask(__name__)# Generate a globally unique address for this nodenode_identifier = str(uuid4()).replace('-', '')# Instantiate the Blockchainblockchain = Blockchain()@app.route('/mine', methods=['GET'])def mine():return "We'll mine a new Block"@app.route('/transactions/new', methods=['POST'])def new_transaction():return "We'll add a new transaction"@app.route('/chain', methods=['GET'])def full_chain():response = {'chain': blockchain.chain,'length': len(blockchain.chain),}return jsonify(response), 200if __name__ == '__main__':app.run(host='0.0.0.0', port=5000)简单的说明一下以上代码:第15行: 创建一个节点.第18行: 为节点创建一个随机的名字.第21行: 实例Blockchain类.第24–26行: 创建/mine GET接口。第28–30行: 创建/transactions/new POST接口,可以给接口发送交易数据.第32–38行: 创建 /chain 接口, 返回整个区块链.第40–41行: 服务运行在端口5000上.发送交易发送到节点的交易数据结构如下:{"sender": "my address","recipient": "someone else's address","amount": 5}之前已经有添加交易的方法,基于接口来添加交易就很简单了import hashlibimport jsonfrom textwrap import dedentfrom time import timefrom uuid import uuid4from flask import Flask, jsonify, request...@app.route('/transactions/new', methods=['POST'])def new_transaction():values = request.get_json()# Check that the required fields are in the POST'ed datarequired = ['sender', 'recipient', 'amount']if not all(k in values for k in required):return 'Missing values', 400# Create a new Transactionindex = blockchain.new_transaction(values['sender'], values['recipient'], values['amount'])response = {'message': f'Transaction will be added to Block {index}'}return jsonify(response), 201挖矿挖矿正是神奇所在,它很简单,做了一下三件事:计算工作量证明PoW通过新增一个交易授予矿工(自己)一个币构造新区块并将其添加到链中import hashlibimport jsonfrom time import timefrom uuid import uuid4from flask import Flask, jsonify, request...@app.route('/mine', methods=['GET'])def mine():# We run the proof of work algorithm to get the next proof...last_block = blockchain.last_blocklast_proof = last_block['proof']proof = blockchain.proof_of_work(last_proof)# 给工作量证明的节点提供奖励.# 发送者为 "0" 表明是新挖出的币blockchain.new_transaction(sender="0",recipient=node_identifier,amount=1,)# Forge the new Block by adding it to the chainblock = blockchain.new_block(proof)response = {'message': "New Block Forged",'index': block['index'],'transactions': block['transactions'],'proof': block['proof'],'previous_hash': block['previous_hash'],}return jsonify(response), 200注意交易的接收者是我们自己的服务器节点,我们做的大部分工作都只是围绕Blockchain类方法进行交互。到此,我们的区块链就算完成了,我们来实际运行下。运行区块链你可以使用cURL 或Postman 去和API进行交互,启动server:$ python blockchain.py* Runing on http://127.0.0.1:5000/ (Press CTRL+C to quit)让我们通过请求 http://localhost:5000/mine 来进行挖矿通过post请求,添加一个新交易如果不是使用Postman,则用一下的cURL语句也是一样的:$ curl -X POST -H "Content-Type: application/json" -d '{"sender": "d4ee26eee15148ee92c6cd394edd974e","recipient": "someone-other-address","amount": 5}' "http://localhost:5000/transactions/new"在挖了两次矿之后,就有3个块了,通过请求 http://localhost:5000/chain 可以得到所有的块信息。{"chain": [{"index": 1,"previous_hash": 1,"proof": 100,"timestamp": 1506280650.770839,"transactions": []},{"index": 2,"previous_hash": "c099bc...bfb7","proof": 35293,"timestamp": 1506280664.717925,"transactions": [{"amount": 1,"recipient": "8bbcb347e0634905b0cac7955bae152b","sender": "0"}]},{"index": 3,"previous_hash": "eff91a...10f2","proof": 35089,"timestamp": 1506280666.1086972,"transactions": [{"amount": 1,"recipient": "8bbcb347e0634905b0cac7955bae152b","sender": "0"}]}],"length": 3}一致性(共识)我们已经有了一个基本的区块链可以接受交易和挖矿。但是区块链系统应该是分布式的。既然是分布式的,那么我们究竟拿什么保证所有节点有同样的链呢?这就是一致性问题,我们要想在网络上有多个节点,就必须实现一个一致性的算法。注册节点在实现一致性算法之前,我们需要找到一种方式让一个节点知道它相邻的节点。每个节点都需要保存一份包含网络中其它节点的记录。因此让我们新增几个接口:/nodes/register 接收URL形式的新节点列表/nodes/resolve 执行一致性算法,解决任何冲突,确保节点拥有正确的链我们修改下Blockchain的init函数并提供一个注册节点方法:...from urllib.parse import urlparse...class Blockchain(object):def __init__(self):...self.nodes = set()...def register_node(self, address):"""Add a new node to the list of nodes:param address: Address of node. Eg. 'http://192.168.0.5:5000':return: None"""parsed_url = urlparse(address)self.nodes.add(parsed_url.netloc)我们用 set 来储存节点,这是一种避免重复添加节点的简单方法。实现共识算法前面提到,冲突是指不同的节点拥有不同的链,为了解决这个问题,规定最长的、有效的链才是最终的链,换句话说,网络中有效最长链才是实际的链。我们使用一下的算法,来达到网络中的共识。...import requestsclass Blockchain(object)...def valid_chain(self, chain):"""Determine if a given blockchain is valid:param chain: A blockchain:return: True if valid, False if not"""last_block = chain[0]current_index = 1while current_index < len(chain):block = chain[current_index]print(f'{last_block}')print(f'{block}')print("-----------")# Check that the hash of the block is correctif block['previous_hash'] != self.hash(last_block):return False# Check that the Proof of Work is correctif not self.valid_proof(last_block['proof'], block['proof']):return Falselast_block = blockcurrent_index += 1return Truedef resolve_conflicts(self):"""共识算法解决冲突使用网络中最长的链.:return: True 如果链被取代, 否则为False"""neighbours = self.nodesnew_chain = None# We're only looking for chains longer than oursmax_length = len(self.chain)# Grab and verify the chains from all the nodes in our networkfor node in neighbours:response = requests.get(f'http://{node}/chain')if response.status_code == 200:length = response.json()['length']chain = response.json()['chain']# Check if the length is longer and the chain is validif length > max_length and self.valid_chain(chain):max_length = lengthnew_chain = chain# Replace our chain if we discovered a new, valid chain longer than oursif new_chain:self.chain = new_chainreturn Truereturn False第一个方法 valid_chain() 用来检查是否是有效链,遍历每个块验证hash和proof;第二个方法 resolve_conflicts() 用来解决冲突,遍历所有的邻居节点,并用上一个方法检查链的有效性,如果发现有效更长链,就替换掉自己的链。让我们添加两个路由,一个用来注册节点,一个用来解决冲突。@app.route('/nodes/register', methods=['POST'])def register_nodes():values = request.get_json()nodes = values.get('nodes')if nodes is None:return "Error: Please supply a valid list of nodes", 400for node in nodes:blockchain.register_node(node)response = {'message': 'New nodes have been added','total_nodes': list(blockchain.nodes),}return jsonify(response), 201@app.route('/nodes/resolve', methods=['GET'])def consensus():replaced = blockchain.resolve_conflicts()if replaced:response = {'message': 'Our chain was replaced','new_chain': blockchain.chain}else:response = {'message': 'Our chain is authoritative','chain': blockchain.chain}return jsonify(response), 200你可以在不同的机器运行节点,或在一台机机开启不同的网络端口来模拟多节点的网络,这里在同一台机器开启不同的端口演示,在不同的终端运行一下命令,就启动了两个节点:http://localhost:5000 和 http://localhost:5001pipenv run python blockchain.pypipenv run python blockchain.py -p 5001然后在节点2上挖两个块,确保是更长的链,然后在节点1上访问接口/nodes/resolve ,这时节点1的链会通过共识算法被节点2的链取代。好啦,你可以邀请朋友们一起来测试你的区块链原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。如有侵权,请联系 cloudcommunity@tencent.com 删除。python区块链flaskhttpgit原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。如有侵权,请联系 cloudcommunity@tencent.com 删除。python区块链flaskhttpgit评论登录后参与评论0 条评论热度最新登录 后参与评论推荐阅读LV.关注文章0获赞0相关产品与服务区块链云链聚未来,协同无边界。腾讯云区块链作为中国领先的区块链服务平台和技术提供商,致力于构建技术、数据、价值、产业互联互通的区块链基础设施,引领区块链底层技术及行业应用创新,助力传统产业转型升级,推动实体经济与数字经济深度融合。产品介绍2024新春采购节领券社区专栏文章阅读清单互动问答技术沙龙技术视频团队主页腾讯云TI平台活动自媒体分享计划邀请作者入驻自荐上首页技术竞赛资源技术周刊社区标签开发者手册开发者实验室关于社区规范免责声明联系我们友情链接腾讯云开发者扫码关注腾讯云开发者领取腾讯云代金券热门产品域名注册云服务器区块链服务消息队列网络加速云数据库域名解析云存储视频直播热门推荐人脸识别腾讯会议企业云CDN加速视频通话图像分析MySQL 数据库SSL 证书语音识别更多推荐数据安全负载均衡短信文字识别云点播商标注册小程序开发网站监控数据迁移Copyright © 2013 - 2024 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有 深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569腾讯云计算(北京)有限责任公司 京ICP证150476号 |  京ICP备11018762号 | 京公网安备号11010802020287问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档Copyright © 2013 - 2024 Tencent Cloud.All Rights Reserved. 腾讯云 版权所有登录 后参与评论00

版权声明:本文由比特派app最新版下载苹果发布,如需转载请注明出处。

本文链接:https://www.siyuewuyu.com/article/521.html