新增内容:构建去中心化网络

import socket #套接字,利用三元组【ip地址,协议,端口】可以进行网络间通信

import threading #线程

import pickle

# 定义一个全局列表保存所有节点

NODE_LIST = []

class Node(threading.Thread): #继承与线程

def __init__(self, name, port, host="localhost"):

threading.Thread.__init__(self, name=name)

self.host = host # 服务器地址,本地电脑都设为localhost

self.port = port # 每个节点对应一个唯一的端口号

self.name = name # 唯一的节点名称

self.wallet = Wallet()

self.blockchain = None # 用来存储一个区块链副本 账本

def run(self):

"""

节点运行

"""

self.init_blockchain() # 初始化区块链

# 在指定端口进行监听

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #获取TCP/ip 套接字sock

sock.bind((self.host, self.port)) #绑定主机,端口号到套接字sock

NODE_LIST.append({

"name": self.name,

"host": self.host,

"port": self.port

})

sock.listen(10) #开始TCP监听

print(self.name, "运行中...")

while True: # 不断处理其他节点发送的请求

connection,address = sock.accept() #被动接受TCP客户的连接,(阻塞式)等待连接的到来

try:

print(self.name, "处理请求内容...")

self.handle_request(connection)

except socket.timeout:

print('超时!')

except Exception as e:

print(e, )

connection.close()

def handle_request(self, connection):

data = []

while True: # 不断读取请求数据直至读取完成

buf = connection.recv(1024)

if not buf: # 若读取不到新的数据则退出

break

data.append(buf)

if len(buf) < 1024: # 若读取到的数据长度小于规定长度,说明数据读取完成,退出

break

t = pickle.loads(b''.join(data)) #从pickle格式的文件中读取数据并转换为Python的类型。

print("数据接受完成,判断数据的 类 :交易,区块,初始化" )

if isinstance(t, Transaction): # 如果t是新交易类 消息

print("处理交易请求...")

if verify_sign(t.pubkey, str(t), t.signature): #验证交易,公钥验证签名

# 验证交易签名没问题,生成一个新的区块

print(self.name, "验证交易成功")

new_block = Block(transactions=[t], prev_hash="")

print(self.name, "生成新的区块...")

w = ProofOfWork(new_block, self.wallet)

block = w.mine() #挖矿,挖到正确的区块哈希值,此处block就是新的区块,主要是找到了符合要求的nonce值

print(self.name, "将新区块添加到区块链中")

self.blockchain.add_block(block)

print(self.name, "将新区块广播到网络中...")

self.broadcast_new_block(block)

else:

print(self.name, "交易验证失败!") #签名不对

elif isinstance(t, Block): #如果t是新区块类 消息

print("处理新区块请求...")

if self.verify_block(t):

print(self.name, "区块验证成功")

self.blockchain.add_block(t)

print(self.name, "添加新区块成功")

else:

print(self.name, "区块验证失败!")

else: # 如果不是新区块消息,默认为初始化消息类型,返回本地区块链内容

print("是我是我,我是初始化,我要返回我的区块链信息")

connection.send(pickle.dumps(self.blockchain))

def verify_block(self, block):

"""

验证区块有效性 是否是符合难度的区块哈希值,找到了正确的nonce值

"""

message = hashlib.sha256()

message.update(str(block.prev_hash).encode('utf-8'))

# 更新区块中的交易数据

# message.update(str(self.block.data).encode('utf-8'))

message.update(str(block.transactions).encode('utf-8'))

message.update(str(block.timestamp).encode('utf-8'))

message.update(str(block.nonce).encode('utf-8'))

digest = message.hexdigest()

prefix = '0' * DIFFICULTY

return digest.startswith(prefix)

def broadcast_new_block(self, block):

"""

将新生成的区块广播到网络中其他节点

"""

for node in NODE_LIST: #遍历节点中的每一个节点,把新的区块广播给除了自己的所有节点

host =node['host']

port = node['port']

if host == self.host and port == self.port:

print(self.name, "忽略自身节点")

else:

print(self.name, "广播新区块至 %s" % (node['name']))

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

sock.connect((host, port)) # 连接到网络中的节点

sock.send(pickle.dumps(block)) # 发送新区块

sock.close() # 发送完成后关闭连接

def init_blockchain(self):

"""

初始化当前节点的区块链

"""

#PER_BYTE = 1024

if NODE_LIST: # 若当前网络中已存在其他节点,则从第一个节点从获取区块链信息

host = NODE_LIST[0]['host']

port = NODE_LIST[0]['port']

name = NODE_LIST[0]["name"]

print(self.name, "发送初始化请求 %s" % (name))

print("开始让节点1发送请求")

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #获取TCP/ip套接字

sock.connect((host, port)) # 连接到网络中的第一个节点

sock.send(pickle.dumps('INIT')) # 发送初始化请求 pickle.dumps将Python数据转换为pickle格式的bytes字串

print("请求成功")

data = []

print("开始接受节点1的connect返回的信息")

while True: # 读取区块链信息,直至完全获取后退出

buf = sock.recv(1024)

print("接收中")

if not buf:

print("接收完毕,接空")

break

data.append(buf)

if len(buf) < 1024:

print("太短了,完毕")

break

sock.close() # 获取完成后关闭连接

# 将获取的区块链信息赋值到当前节点

self.blockchain = pickle.loads(b''.join(data))

print(self.name, "初始化完成.")

else:

# 如果是网络中的第一个节点,初始化一个创世区块

block = Block(transactions=[], prev_hash="")

w = ProofOfWork(block, self.wallet)

genesis_block = w.mine()

self.blockchain = BlockChain()

self.blockchain.add_block(genesis_block)

print("生成创世区块")

def submit_transaction(self, transaction): #遍历节点中的每一个节点,把新的交易广播给除了自己的所有节点

for node in NODE_LIST:

host =node['host']

port = node['port']

if host == self.host and port == self.port:

print(self.name, "忽略自身节点")

else:

print(self.name, "广播新区块至 %s:%s" % (self.host, self.port))

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

sock.connect((node["host"], node["port"]))

sock.send(pickle.dumps(transaction))

sock.close()

def get_balance(self):

balance = 0

for block in self.blockchain.blocks:

for t in block.transactions:

if t.sender == self.wallet.address.decode():

balance -= t.amount

elif t.recipient == self.wallet.address.decode():

balance += t.amount

print("当前拥有%.1f个加密货币" % (balance))

def print_blockchain(self):

print("区块链包含区块个数: %d\n" % len(self.blockchain.blocks))

for block in self.blockchain.blocks:

print("上个区块哈希:%s" % block.prev_hash)

print("区块内容:%s" % block.transactions)

print("区块哈希:%s" % block.hash)

print("\n")

# 初始化节点1

node1 = Node("节点1", 8000)

node1.start() #启动线程 调用 start() 方法是用来启动线程的,轮到该线程执行时,会自动调用 run();直接调用 run() 方法

node1.print_blockchain() #输出区块信息

node2 = Node("节点2", 8001)

node2.start()

node2.print_blockchain()

node1.get_balance()

node2.get_balance()

#创建交易

new_transaction = Transaction(

sender=node1.wallet.address,

recipient=node2.wallet.address,

amount=0.3

)

sig = node1.wallet.sign(str(new_transaction)) #私钥签名

new_transaction.set_sign(sig, node1.wallet.pubkey)#发送公钥,和签名,给验证者验证

node1.submit_transaction(new_transaction) #广播交易

node1.print_blockchain()

node2.print_blockchain()

node1.get_balance()

node2.get_balance()

精彩文章

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。