1 区块链基础

1.1 什么是区块链

区块链(Blockchain)是一种去中心化的分布式账本技术,它通过密码学和共识算法等技术手段,保证了数据在网络中的不可篡改性和安全性,使得区块链数据可以公开透明地被多个参与者共享、存储和验证,从而实现了一种去信任化的分布式数据管理方式。

1.2 区块链的特点

区块链的主要特点包括:

  1. 去中心化:区块链的数据存储在网络中的多个节点上,没有中心化的管理机构,所有参与者都有权利共同维护和管理网络。
  2. 不可篡改:区块链使用密码学技术和共识算法等手段,确保数据的不可篡改性和安全性。
  3. 共识机制:区块链采用共识机制来解决数据一致性问题,确保所有参与者都能够达成共识,保证数据的真实性和可靠性。
  4. 匿名性:区块链上的交易和数据记录是匿名的,保护了用户的隐私和安全。
  5. 开放性:区块链是一种开放的技术,任何人都可以参与其中,共同维护和管理网络。

1.3 区块的数据结构

image-20230306190805778

  • 版本号(Version Number):指定了当前区块链协议的版本号,每个区块链协议的版本都有相应的规范和文档说明,版本号用于指定使用哪个版本的规范。
  • 前一区块哈希值(Previous Block Hash):指向前一个区块的哈希值,通过这个参数,区块链头将多个区块链接在一起形成链式结构,从而确保了区块链的不可篡改性和完整性。
  • Merkle 根哈希值(Merkle Root Hash):Merkle 根是由区块中所有交易的哈希值构成的二叉树根节点的哈希值,它可以确保区块中所有交易的完整性。
  • 时间戳(Timestamp):指定了当前区块被创建的时间,一般使用UTC时间。
  • 难度目标(Difficulty Target):用于控制区块链中的挖矿难度,难度目标的设置可以影响到区块链的安全性和运行效率。
  • 随机数(Nonce):通过不断地改变随机数的值,矿工可以尝试不断地寻找符合当前难度目标的哈希值,以获得新的区块奖励。
  • 交易信息(Transaction):每一条交易信息对应一笔用户交易

通过一块又一块的区块通过 Previous Block Hash 相链接,最终组成了一条区块链:

在这里插入图片描述

1.4 Merkle 树

刚刚我们在区块头中提到了(Merkle Tree),在区块链的构建中,。

1.4.1 Merkle 树的概念

默克尔树(Merkle Tree)其实并不是一个真实存放在区块链头部的数据结构,Merkle树仅保留树根的值,那么为什么要这么设计呢?

假想一个区块中共保留了 1000 条交易记录,对于这些交易记录,每一条都有可能被篡改的风险,为了防止篡改的出现,我们可以通过哈希算法将交易记录的数据映射为哈希值,这样就算是交易记录被篡改,收到该笔交易信息并核查交易的用户同样可以通过哈希算法算出一个新的哈希值与旧的哈希值进行比对,从而判断交易是否被修改。

但是这样做需要每一笔交易都将旧的哈希值存放在交易体中以便比对,这样不仅占用了更多的空间同时也不易于比对,这时 Merkle 树的作用就体现出来了。

如果将每条交易的哈希值看做一个叶子节点,从下往上将两个相邻叶子节点的组合再次进行哈希运算得到新的哈希值,新的哈希值成为树节点继续与相邻的树节点组合成新的哈希值。

img

在重复一定次数后直到形成唯一的根节点。这个唯一的根节点也就是保存在区块头部的 Merkle 根。

1.4.2 Merkle 树完整性的验证

验证 Merkle 树的完整性通常需要使用 Merkle 根(Merkle Root)来进行比对。在区块链中,每个区块的交易数据都被组织成 Merkle 树,并且每个区块头都包含了一个 Merkle 根的哈希值。通过比对这个哈希值,可以验证 Merkle 树的完整性。

具体的验证过程可以如下:

  1. 从区块头中获取 Merkle 根的哈希值。
  2. 计算当前区块中所有交易数据的哈希值,并将这些哈希值按照事先规定的顺序组成一个 Merkle 树。
  3. 最终的 Merkle 根节点的哈希值就是当前区块头中的 Merkle 根哈希值,如果这两个哈希值不相同,说明 Merkle 树已经被修改过,当前区块数据可能已经被篡改。

这样一来通过交易记录生成的新 Merkle根与旧 Merkle根的比对,就可以判断交易记录是否被更改。

1.4.3 Merkle 树的Java实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package com.dce.blockchain.web.util;

import com.dce.blockchain.web.entity.Transaction;

import java.util.ArrayList;
import java.util.List;

/**
* @program: blockChain-java
* @description: 默克尔树的实现
* @author: CuiJieXiang_1023
* @create: 2023-03-04 13:34
**/
public class MerkleTree {
private List<String> txs;
private String root;

/**
*
* @param transactions 放入该区块的交易记录
*/
public MerkleTree(List<Transaction> transactions) {
txs = new ArrayList<>();
for (int i = 0; i < transactions.size(); i++) {
txs.add(transactions.get(i).transactionId);
}
root = "";
}

/**
* 构造Merkle Tree,返回根节点
* @return
*/
private String buildTree() {
if (txs.isEmpty()) {
return null;
}
if (txs.size() == 1) {
return txs.get(0);
}
List<String> tempTxs = new ArrayList<>(txs);

List<String> newTxs = getNewTxList(tempTxs);
while (newTxs.size() > 1) {
newTxs = getNewTxList(newTxs);
}

root = newTxs.get(0);
return root;
}

/**
* 构造 Merkle Tree
* @param txs
* @return
*/
private List<String> getNewTxList(List<String> txs) {
List<String> newTxs = new ArrayList<>();
int index = 0;
while (index < txs.size()) {
String left = txs.get(index);
index++;

String right = "";
if (index != txs.size()) {
right = txs.get(index);
}
// 新的Merkle树节点
newTxs.add(CryptoUtil.applySha256(left + right));
index++;
}
return newTxs;
}

/**
* 获取交易的merkle树的根节点,当一个区块中包含大量的transaction时,计算所有的hash值是不可取的
* 所以使用merkel tree对全部hash值进行一个计算。
*
* @param transactions
* @return
*/
public static String getMerkleRoot(List<Transaction> transactions) {
MerkleTree merkleTree = new MerkleTree(transactions);
return merkleTree.buildTree();
}
}

2 区块链的相关概念

2.1 加密算法

2.1.1 加密算法的分类

  • 私用密钥加密技术对称式加密(Symmetric Key Encryption):对称式加密方式对加密和解密使用相同的密钥。通常,这种加密方式在应用中难以实施,因为用同一种安全方式共享密钥很难。如:RC4、RC2、DES 和 AES 系列加密算法;

  • 公开密钥加密技术非对称密钥加密(Asymmetric Key Encryption):非对称密钥加密使用一组公共/私人密钥系统,加密时使用一种密钥,解密时使用另一种密钥。公共密钥可以广泛的共享和透露。当需要用加密方式向服务器外部传送数据时,这种加密方式更方便。如:RSA;

2.1.2 区块链中非对称算法

非对称加密算法 — ECC

椭圆曲线加密算法(ECC)是一种公钥加密体制,最初由 Koblitz 和 Miller 两人于1985年提出,其数学基础是利用椭圆曲线上的有理点构成Abel加法群上椭圆离散对数的计算困难性。

简单来说,它就是通过在一个特定的椭圆曲线上选取点,利用这些点的数学特性来实现加密和解密的过程。与传统的加密算法相比,ECC 具有更高的安全性和更短的密钥长度,能够更好地保护数据的安全性。

椭圆曲线加密算法原理

非对称加密算法 —— RSA

RSA可以被用于公钥密码和数字签名。RSA是被研究得最广泛的公钥算法,从提出到现在已近三十年,经历了各种攻击的考验,逐渐为人们接受,普遍认为是目前最优秀的公钥方案之一。

RSA算法原理

ECC与RSA算法的比较

ECC和RSA相比,在许多方面都有对绝对的优势,主要体现在以下方面:

  • 抗攻击性强。相同的密钥长度,其抗攻击性要强很多倍。
  • 计算量小,处理速度快。ECC总的速度比RSA、DSA要快得多。
  • 存储空间占用小。ECC的密钥尺寸和系统参数与RSA、DSA相比要小得多,意味着它所占的存贮空间要小得多。这对于加密算法在IC卡上的应用具有特别重要的意义。
  • 带宽要求低。当对长消息进行加解密时,三类密码系统有相同的带宽要求,但应用于短消息时ECC带宽要求却低得多。带宽要求低使ECC在无线网络领域具有广泛的应用前景。

下面两张表示是RSA和ECC的安全性和速度的比较,可以看到RSA算法生辰的秘钥其安全性更高,但代价是秘钥长度更长。

img

2.1.3 散列算法

散列算法,又称哈希算法,把任意长的输入字符串变化成固定长的输出字符串的一种函数,是一种从任何一种数据中创建小的数字“指纹”的方法。散列函数散把消息或数据压缩成摘要,使得数据量变小,将数据的格式固定下来。该函数将数据打乱混合,重新创建一个叫做散列值(或哈希值)的指纹。散列值通常用一个短的随机字母和数字组成的字符串来代表。

散列算法——SHA-256算法

SHA - 256 算法可以将任意长度的数据映射为256 bit长的哈希值,比特币中的散列值就是通过SHA-256算法所求。

SHA256算法原理详解

散列算法——MD5

MD5将任意长度的“字节串”变换成一个128bit的大整数,并且它是一个不可逆的字符串变换算法

MD5算法原理讲解

2.2 公钥与私钥

在区块链技术中经常听说公钥与私钥,其实他们都是非对称加密算法的产物。私钥不能被他人知晓,公钥可以公开。

2.2.1 私钥

私钥就是一个随机选出来的数字,可通过伪随机数生成器(Pseudo-random Number Generator)生成。比特币地址中的资金取决于对私钥的控制,你拥有私钥就相当于你用这个私钥下的地址的所有的比特币。所以必须对私钥保密以及防止丢失(难以复原),一旦丢失,其地址下的比特币也相当于没了。

一个随机数生成一个私钥是最重要的一步,需要找到足够的熵源,即随机性来源。比特币私钥本质上是与从1到2^256之间选一个数字无异,只要选取的结果是不可预测或者不可重复即可。比特币是通过操作系统底层产生256位随机数

私钥可以是1和n-1之间的任何数字,其中n是一个常数(n=1.158 *10^77,略小于 2^256)。要生成这样的一个私钥,要随机选择一个256位的数字, 并检查它是否小于n-1。从编程的角度来看,一般是通过在一个密码学安全的随机源中取出一长串随机字节,对其使用SHA256哈希算法进行运算,这样就可以方便地产生一个256位的数字。如果运算结果小于n-1,我们就有了一个合适的私钥。否则,就用另一个随机数再重复一次。

警告:不要自己写代码或使用你的编程语言提供的简易随机数生成器来获得一个随机数。使用密码学安全的伪随机数生成器(CSPRNG),并且需要有一个来自具有足够熵值的源的种子。使用随机数发生器的程序库时,需仔细研读其文档,以 确保它是加密安全的。正确实施CSPRNG是密钥安全性的关键所在。

以下是一个随机生成的私钥(k),以十六进制格式表示(256位的二进制数,以64位十六进制数显示,每个十六进制数占 4 位):

1
1E99423A4ED27608A15A2616A2B0E9E52CED330AC530EDCC32C8FFC6A526AEDD

提示比特币私钥空间的大小是 2^256,这是一个非常大的数字。用十进制表示的 话,大约是 10^77,而可见宇宙被估计只含有 10^80 个原子。

2.2.2 公钥

通过椭圆曲线乘法(ECC) 可以从私钥计算得到公钥,这是不可逆转的过程:K=k*G 。 其中k是私钥,G是被称为生成点的常数点,而K是所得公钥。其反向运算,被称为“寻找离散对数”——已知公钥K来求出私钥k——是非常困难的,就像去试验所有可能的k值,即暴力搜索。

提示:椭圆曲线乘法是密码学家称之为“陷阱门”功能的一种函数:在一个方向(乘 法)很容易做,而不可能在相反的方向(除法)做。 私钥的所有者可以容易地创建公钥,然后与世界共享,知道没有人可以从公钥中反转函数并计算出私钥。 这 个数学技巧成为证明比特币资金所有权的不可伪造和安全的数字签名的基础。

2.2.3 公私钥与加密

公钥与私钥的出现,使去中心化数字加密信息传输成为了可能,下面举一个例子:

假如 A 与 B 进行通信,A在发送消息时:A使用B的公钥进行加密,B在接收信息时:使用自己的私钥进行解密,这样谁都无法截取到 A 对 B 的通信具体内容,只有拥有 B 秘钥的人才能将加密的信息解密。

为什么不使用私钥加密?

因为使用私钥加密之后,拥有公钥的人都可以进行解密,那岂不是泄露了 A 和 B 之间的消息了。而使用对方的公钥加密,那么就只有对方的私钥可以解密,也就是说不会有第三方可以解密内容

2.2.4 公私钥与数字签名

在区块链中,公钥和私钥被广泛应用于身份认证和交易验证。例如,在比特币中,当用户要花费自己的比特币时,需要用私钥对交易进行签名,以证明自己是交易的合法拥有者。加签名的目的是为了防止他人随意使用他人数字货币,同时能够让接收到消息的一方确认该交易确实是由私钥的拥有者发起。

假设 A 向 B 转账,因为私钥只存在于A,也只有A才能对消息进行加签,这个过程就叫签名。公钥的作用就是验签,验证了就能知道消息的真伪,任何收到该转账信息 的一方,都可以通过 A 的公钥来验证签名的真伪。

总结来说: 公钥和私钥的作用主要就是用来加解密和加验签:

  • 公钥加密、私钥解密(传输加密数据)
  • 私钥加签、公钥验签(验证信息来源)

2.3 区块链共识机制

共识机制是区块链一大知识领域, 通过算法和协议来保证各个节点对数据的一致性,确保所有节点都同意哪些数据可以被添加到区块链中。早在区块链之前,就存在各种分布式的共识机制,共识机制不是因区块链所发明,但区块链却对共识机制推广和进步有着重要影响。

工作量证明机制(PoW)

PoW(Proof of Work),即工作量证明,闻名于比特币,俗称”挖矿”。PoW是指系统为达到某一目标而设置的度量方法。简单理解就是一份证明,用来确认你做过一定量的工作。监测工作的整个过程通常是极为低效的,而通过对工作的结果进行认证来证明完成了相应的工作量,则是一种非常高效的方式。PoW是按劳分配,算力决定一起,谁的算力多谁记账的概率就越大,可理解为力量型比较。

工作量证明(PoW)通过计算一个数值( nonce ),挖矿节点需要找到一个符合特定条件的哈希值。这个条件是,计算出来的哈希值必须小于一个固定的值(中本聪的论文中说是要有一定数量的前置‘0’),这个值称为**难度目标(difficulty)**。为了找到这个哈希值,挖矿节点需要反复尝试不同的哈希值,这就是所谓的计算工作量证明。这个过程需要耗费大量的计算资源和时间。

如果验证通过,则表明已经有节点成功解迷,自己就不再竞争当前区块打包,而是选择接受这个区块,记录到自己的账本中,然后进行下一个区块的竞争猜谜。网络中只有最快解谜的区块,才会添加的账本中,其他的节点进行复制,这样就保证了整个账本的唯一性。

假如节点有任何的作弊行为,都会导致网络的节点验证不通过,直接丢弃其打包的区块,这个区块就无法记录到总账本中,作弊的节点耗费的成本就白费了,因此在巨大的挖矿成本下,也使得矿工自觉自愿的遵守比特币系统的共识协议,也就确保了整个系统的安全。

需要注意的是,难度目标是动态调整的,目的是保持比特币的产生速度稳定(大约10分钟产出新区块)。如果比特币网络中的挖矿节点数量增加,难度目标也会随之提高,反之亦然。这个机制保证了比特币的总产量不会超过2100万个,同时也保证了比特币的产生速度是可控的。

PoW 工作流程

在这里插入图片描述
从流程图中看出,pow工作量证明的流程主要经历三步:

  1. 生成Merkle根哈希
    即节点自己生成一笔筹币交易–,并且与其他所有即将打包的交易通过Merkle树算法生成Merkle根哈希,所以为什么说区块是工作量证明的三要素之一。

  2. 组装区块头
    区块头将被作为计算出工作量证明输出的一个输入参数,因此第一步计算出来的Merkle根哈希和区块头的其他组成部分组装成区块头。

  3. 计算出工作量证明的输出
    下面我们直接通过公式和一些伪代码去理解工作量证明的输出:

       i. 工作量证明的输出=SHA256(SHA256(区块头))

       ii. if(工作量证明的输出有一定数量的前置‘0’),证明工作量完成

       iii.if(上述条件不成立),变更随机数,递归i的逻辑,继续与目标值比对。

上面的流程图及解析即pow工作量证明的整个过程。

PoW记账共识

前面三部分中讲解的是单节点工作量证明流程,有了这个计算流程,我们就得将其使用起来,在比特币平台中,中本聪就是运用的pow工作量证明来使全网节点达到51%及以上的共识记账,以下将介绍pow工作量证明共识是如何记账的?

交易产生到被加入区块的流程:

首先,客户端产生新的交易,向全网广播

第二,每个节点收到请求,将交易纳入区块中

第三,每个节点通过第三章中描述的pow工作量证明

第四,当某个节点找到了证明,向全网广播

第五,当且仅当该区块的交易是有效的且在之前中未存在的,其他节点才认同该区块的有效性

第六,接受该区块且在该区块的末尾制造新的区块

大概时序图如下:

在这里插入图片描述

Pow 优缺点

通过上面的描述,PoW优点很明显:

  1. 完全去中心化(任何人都可以加入);
  2. 节点自由进出,容易实现;
  3. 破坏系统花费的成本巨大;

关于破坏系统成本巨大可以分两层意思理解:

  1. 在指定时间内,给定一个难度,找到答案的概率唯一地由所有参与者能够迭代哈希的速度决定。与之前的历史无关,与数据无关,只跟算力有关。
  2. 掌握51%的算力对系统进行攻击所付出的代价远远大于作为一个系统的维护者和诚实参与者所得到的。

PoW缺点也相当明显:

  1. 对节点的性能网络环境要求高;
  2. 浪费资源;
  3. 每秒钟最多只能做七笔交易,效率低下;
  4. 矿场的出现违背了去中心的初衷;
  5. 不能确保最终一致性;
  6. 比特币产量每4年减半,利益驱动性降低导致旷工数量减少从而导致比特币网络瘫痪。

股权证明机制(PoS)

PoS 共识机制工作原理

PoS(Proof of Stake)股权证明机制,与PoW相比,不需要证明你在记账前做了某项工作,而是证明你拥有某些财产。股权决定一起,谁的股权大,谁记账的概率就越大。相较于消耗大量算力的 PoW 共识机制,PoS 共识机制筛选旷工的方式更为简单。

首先 PoW 提出币龄这个概念,币龄 = 持有的货币数 * 持有时间,比如说我有100币,持有了30天,那么我的币龄就是3000。币龄越大的节点呢获取记账权(也就是生成区块)的概率越大,获取记账权力的节点需要将其所有币龄作为抵押保证生成区块不会出错。每次记完账之后,该节点的币龄就清空,每清空 365 个币龄,你将会从区块中获取到清空币龄 5% 的货币奖励,下面简述一下工作流程:

  1. 加入PoS的都是持币人并称为验证者。
  2. 在验证者中挑一个持币人给予权利生成新的区块,挑选顺序依据持币的多少。
  3. 其余的持币人,也就是验证者进行对区块进行验证。验证通过生成新的区块。
  4. 如果在一定时间内没有生成新区块,PoS会挑选下一个验证者,给予生成新区块的权利。
  5. 以此类推,以区块链中最长的链为准。

PoS 共识机制的优点

优点

  1. 节省能耗,相比于PoW的能源消耗,PoS大大缩减了能源消耗,简单来说就是省电。
  2. 入门门槛低,用户参与度更高,只需要一台电脑,就可以称为验证者。
  3. 在一定程度上缩短了共识达成时间。

在PoS中,节点拥有的币越多,设计利益越多,节点反而会愿意去维护这个系统的稳定。他们不会进行恶意攻击损害自己的利益。PoS可以更有效地防御51%算力攻击。

PoS 共识机制的缺点

  • 我们再从pos的实现算法公式去看,币龄的计算公式中,假如一开始挖矿,只有创始区块中有币,也就是说其他矿机是没法参与挖矿的,因为币的个数这个值对他们来说永远是零,这也就是pos机制的缺陷之一币无法发行的问题。

  • 同样是从pos的实现算法公式可以分析到,币龄其实就是时间,一旦挖矿者囤积一定的币,很久很久之后发起攻击,这样他也将很容易拿到记账权,所以我们得给每个币设计一个时间上限(通常是90天)。

  • 设计时间上限后,虽然解决掉了部分挖矿者囤积币的缺陷,从公式中仍然看到还会面临一个问题,也就是币的数量这个因素还是会影响我们拿到记账权,很多挖矿者还会囤积代币,给代币造成流通上的缺陷。目前有些平台引入币龄按时间衰弱的方案来解决这一缺陷(例如:瑞迪币)。

2.4 P2P网络

区块链的通信网络并不是我们常见的C\S客服端服务器模式,而是采用 P2P(Peer to Peer) 网络,即使用对等网络进行通信。在P2P网络中没有中心服务器,依靠的是用户群体之间相互交换信息,可以理解为:每一个用户既是用户端,也是服务器端;既可以请求服务,同时也要提供服务。

为何使用P2P网络?

在传统的C\S网络中,中心化的服务器就像是枢纽站一样,掌控着所有用户的数据,管理很方便,同时缺点是显而易见的:一旦崩溃的话,就会导致全网的服务挂掉,比如之前的鹿晗事件导致微博服务器瘫痪在安全性方面,如果中央服务器被黑客入侵,或者病毒感染,就能够很快的将病毒辐射到所有的客户端用户,如果用户数量很庞大的话,甚至会影响到社会的稳定。中央服务器对用户数据的掌控性太强,如果一个商家道德底线过低,可能会肆意分析、售卖我们的数据;这些都是中心化服务器的潜在问题;

img

而对等网络(P2P)的出现,其初衷便站在了上述那种中心化网络的的对立面。在对等网络里,每一个网络节点,所具有的功能,在逻辑上是完全对等的,全网无特殊节点,不存在谁是服务端,谁是客户端;每一个节点在对外提供服务的时候,也在使用别的节点为自己提供类似的服务;在P2P网络中,每个网络节点,具有相同的数据收发权限,也就是每一个节点都可以对外提供全网所需的全部服务;也正是因为这,任何一个节点垮掉,都不会对整个网络的稳定性构成威胁。

区块链系统之所以选择P2P作为其组网模型,就是因为两者的出发点都是去中心化,可以理解为网络的主动权不存在与任何一个企业,主动权全在用户手中。中本聪在白皮书中提过,在电子现金系统中,第三方系统是多余的,没有价值,意思就是整个系统不要依赖任何特殊的第三方来完成自身系统的运转。P2P网络的优势就是全网平等、无特殊节点,两者的思想高度契合,P2P技术也已发展成熟,所以对于区块链来说是一大利器。并且如果实现真正意义上点对点式的交易,那么无疑是一个突破性的技术,我们普通人可以省下一大笔之前被中介拿走的“中间费”,在安全性能方面也将更加可靠。

由于对等网络,全网无特殊节点,每个节点都可以提供全网所需的全部服务,没有中心节点把控全网发号施令,保证了数据的自由流通,平等手法,保证了区块链系统在底层通信信道上的平等性,正是因为P2P网络的这些优势特性,可以说对等网络奠定了区块链系统的重要基石;

P2P网络的拓扑形式

第一阶段:集中式

img

​ 最简单的路由方式就是集中式,即存在一个中心节点保存了其他所有节点的索引信息,索引信息一般包括节点 IP 地址、端口、节点资源等。集中式路由的优点就是结构简单、实现容易。但缺点也很明显,由于中心节点需要存储所有节点的路由信息,当节点规模扩展时,就很容易出现性能瓶颈;而且也存在单点故障问题。

第二阶段:纯分布式(随机网络)

​ 在 P2P 节点之间建立随机网络,就是在一个新加入节点和 P2P 网络中的某个节点间随机建立连接通道,从而形成一个随机拓扑结构。新节点加入该网络的实现方法也有很多种,最简单的就是随机选择一个已经存在的节点并建立邻居关系。像比特币的话,则是使用 DNS 的方式来查询其他节点,DNS 一般是硬编码到代码里的,这些 DNS 服务器就会提供比特币节点的 IP 地址列表,从而新节点就可以找到其他节点建立连接通道。新节点与邻居节点建立连接后,还需要进行全网广播,让整个网络知道该节点的存在。全网广播的方式就是,该节点首先向邻居节点广播,邻居节点收到广播消息后,再继续向自己的邻居节点广播,以此类推,从而广播到整个网络。这种广播方法也称为** 泛洪机制**。

​ 纯分布式结构不存在集中式结构的单点性能瓶颈问题和单点故障问题,具有较好的可扩展性,但泛洪机制引入了新的问题,主要是可控性差的问题,包括两个较大的问题:

  • 容易形成泛洪循环,比如节点 A 发出的消息经过节点 B 到 节点 C,节点 C 再广播到节点 A,这就形成了一个循环;
  • 响应消息风暴问题,如果节点 A 想请求的资源被很多节点所拥有,那么在很短时间内,会出现大量节点同时向节点 A 发送响应消息,这就可能会让节点 A 瞬 间瘫痪。

第三阶段:半分布式拓扑

​ 就是混合了集中式和分布式结构,如下图所示:

image-20230323164014987

​ 网络中存在多个超级节点组成分布式网络,而每个超级节点则有多个普通节点与它组成局部的集中式网络。一个新的普通节点加入,则先选择一个超级节点进行通信,该超级节点再推送其他超级节点列表给新加入节点,加入节点再根据列表中的超级节点状态决定选择哪个具体的超级节点作为父节点。这种结构的泛洪广播就只是发生在超级节点之间,就可以避免大规模泛洪存在的问题。在实际应用中,混合式结构是相对灵活并且比较有效的组网架构,实现难度也相对较小,因此目前较多系统基于半分布式拓扑进行开发实现。其实,比特币网络如今也是这种结构

第四阶段:结构化模型

​ 它也是一种分布式网络结构,但与纯分布式结构不同。纯分布式网络就是一个随机网络,而结构化网络则将所有节点按照某种结构进行有序组织,比如形成一个环状网络或树状网络。而结构化网络的具体实现上,普遍都是基于 DHT(Distributed Hash Table,分布式哈希表) 算法思想。

DHT是在BitTorrent、IPFS网络中采用的P2P协议,DHT通过去中心化的方式存下了一个key-value表,key是文件的hash,value是文件所在节点的ip:port

  1. 每个节点任选一个hash值作为节点id
  2. 每个节点仅存hash值和自己id接近的key及其value
  3. 每个节点会维护自己所有peer的ip:port及其id
  4. 当检索一个key的时候,通过multi-hop search找到节点

​ DHT 主要想解决如何在分布式环境下快速而又准确地路由、定位数据的问题。具体的实现方案有 Chord、Pastry、CAN、Kademlia 等算法,其中 Kademlia 也是以太坊网络的实现算法,很多常用的 P2P 应用如 BitTorrent、电驴等也是使用 Kademlia。

img

​ 在 P2P 网络中,可以抽象出两种空间:资源空间节点空间

​ 资源空间就是所有节点保存的资源集合,节点空间就是所有节点的集合。对所有资源和节点分别进行编号,如把资源名称或内容用 Hash 函数变成一个数值(这也是 DHT 常用的一种方法),这样,每个资源就有对应的一个 ID,每个节点也有一个 ID,资源 ID 和节点 ID 之间建立起一种映射关系,比如,将资源 n 的所有索引信息存放到节点 n 上,那要搜索资源 n 时,只要找到节点 n 即可,从而就可以避免泛洪广播,能更快速而又准确地路由和定位数据。当然,在实际应用中,资源 ID 和节点 ID 之间是无法做到一一对应的,但因为 ID 都是数字,就存在大小关系或偏序关系等,基于这些关系就能建立两者的映射关系。这就是 DHT 的核心思想。DHT 算法在资源编号和节点编号上就是使用了分布式哈希表,使得资源空间和节点空间的编号有唯一性、均匀分布式等较好的性质,能够适合结构化分布式网络的要求。

比特币中的P2P网络

2.5 交易与验证

2.5.1 Wallet 钱包

区块链钱包是一种数字货币的存储和管理工具,它的主要作用是存储用户的私钥和公钥,以便用户可以进行数字货币的发送和接收。钱包内部采用不同的数据结构来存储用户的私钥和公钥,并将其转换成钱包地址来进行标识。

1.1 Wallet数据结构

下面提供我在做项目时所采用的Wallet数据结构帮助你更好地理解Wallet:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.dce.blockchain.web.entity;

/**
* Created by simon on 2023/2/8.
*/
@Slf4j
@Getter
public class Wallet {
// 钥匙
private PrivateKey privateKey;

private PublicKey publicKey;

// 钱包地址
private String walletAddress;

// 余额
private double balance = 0f;

private String privateKeyStr;

}

以下是这个钱包的数据结构的详细说明:

  • privateKey: 私钥,类型为PrivateKey,用于对交易数据进行签名。
  • publicKey: 公钥,类型为PublicKey,用于验证交易签名和生成钱包地址。
  • walletAddress: 钱包地址,类型为String,是公钥的哈希值,用于标识钱包。
  • balance: 余额,类型为double,用于记录钱包的数字货币余额。
  • privateKeyStr: 私钥字符串,类型为String,用于方便地输出或存储私钥。

1.2 walletAddress钱包地址

通常情况下一笔交易的产生,用户需要在区块链网络中向某个用户发起一个交易请求,为了标识出交易的用户,我们需要一个数据作为交易的媒介,例如在现实生活中银行卡转账,我们需要的媒介是交易双方的银行卡号,标识卡一向卡二发起一笔转账。而区块链网络中,代表一个用户进行这笔交易的媒介,我们成为钱包地址,通俗理解就可以称为区块链网络中的“银行卡号”。

钱包地址生成逻辑

区块链钱包Wallet的钱包地址是通过公钥生成的。生成过程可以简述为:

  1. 随机生成一个私钥,由256位随机数构成
  2. 使用椭圆曲线算法或者RSA算法,通过私钥计算得到对应的公钥
  3. 使用散列算法(如SHA-256)对公钥进行散列,得到钱包地址

钱包地址的长度和格式取决于使用的加密货币,主流的加密货币地址一般是26-35个字符,以字母和数字组成。

钱包地址的作用
  1. 作为用户接收付款的账户,其他人可以通过钱包地址将资金转入该地址
  2. 保护用户的私钥和身份隐私。钱包地址不会泄露用户私钥信息。

通过以上介绍,我们了解到Wallet作为一个重要的区块链工具,内部主要由私钥和公钥构成,可以生成钱包地址来保护资金和身份,并具有多重重要作用。理解Wallet的原理有助于我们更好地使用区块链技术。

为什么不是公钥交易而是钱包地址

使用钱包地址而不是公钥进行区块链交易有以下几个原因:

  1. 钱包地址相对于公钥更容易识别。钱包地址通常是一个以特定字符开头的字符串,比如以”1”或”3”开头的比特币地址。这些特定字符可以帮助用户快速识别钱包地址,而公钥是一种较长的字符串,不方便用户记忆和使用。
  2. 钱包地址可以更好地保护用户隐私。公钥是公开的,任何人都可以看到它。如果使用公钥进行交易,那么交易记录中会暴露用户的公钥,这可能导致用户的隐私泄露。而钱包地址是由公钥生成的,只有用户本人拥有其私钥才能使用该钱包地址进行交易,从而更好地保护用户的隐私。
  3. 钱包地址可以更好地防止人为错误。公钥是一种较长的字符串,容易出现输入错误,特别是在手动输入的情况下。而钱包地址通常会有一些特定字符或校验位,可以帮助检测输入错误,从而防止交易失败或资金丢失。

2.5.2 UTXO机制

2.1 UTXO 概述

UTXO(Unspent Transaction Output,未花费交易输出)是比特币和其他基于比特币协议的加密货币所使用的一种账户模型。

在UTXO机制中,每一笔交易输出都是一个未花费交易输出(UTXO),每一个未花费的输出都可以被用于下一笔交易的输入,而每一笔交易的输入都会指向之前某个交易的UTXO。因此,UTXO可以被看作是一个被锁定的“金库”,只有拥有相应私钥的人才能解锁并使用其中的数字货币。

举个例子,假设Alice要向Bob转账1个比特币。在这个过程中,Alice需要从之前的某笔交易中获取一个或多个UTXO,然后将其作为输入添加到新的交易中。Bob会在新的交易中得到一笔未花费的输出,也就是一个新的UTXO,他可以在之后的交易中使用这个UTXO。

2.2 UTXO 机制的优点

  • UTXO机制的优点之一是它可以有效地避免双重支付问题,因为每个UTXO只能被使用一次。
  • UTXO机制可以让比特币协议更加轻量级,因为它只需要跟踪未花费的交易输出而不需要维护每个账户的余额信息。

需要注意的是,由于UTXO是一种基于交易的账户模型,因此它与传统的基于账户的账户模型有所不同。在UTXO机制中,一个人的余额并不是存储在一个单独的账户中,而是由该人拥有的UTXO的总和决定。因此,要计算某个人的余额,需要遍历整个交易历史并计算其拥有的所有UTXO的价值总和。

2.3 UTXO交易细节

在UTXO(Unspent Transaction Output,未花费交易输出)机制中,每个UTXO都代表了一笔未花费的交易输出,用于记录某个地址的余额。因此,当你需要支付2元的时候,你需要找到一些UTXO,它们的总金额大于或等于2元,然后构造一个交易,将这些UTXO作为输入,输出2元给对方地址,以及剩余的UTXO找零给自己地址。

如果你只有5元的UTXO,而需要支付2元,则需要找到一笔金额大于等于2元的UTXO,例如3元的UTXO。这时,你可以构造一个交易,将这笔3元的UTXO作为输入,输出2元给对方地址,以及1元的UTXO找零给自己地址。这个过程通常由钱包程序自动完成。

如果你没有一笔金额大于等于2元的UTXO,但是有多笔小额UTXO,可以将它们合并成一笔大额UTXO,然后再进行支付。合并UTXO需要构造一笔新的交易,将多笔小额UTXO作为输入,将它们的总金额输出给一个新的地址,然后将这个新地址的UTXO作为找零输出给自己地址。注意,合并UTXO会增加交易大小和手续费,因此应该尽量避免。

在比特币交易中,”找零” 是指当你用一笔交易支付一定金额时,实际支付的金额比交易输入的总金额要少,因为一部分金额被作为交易费用而支付给矿工。在这种情况下,找零就是将未花费的交易输出返回给你自己,以便你可以在将来的交易中使用它们。

在实现交易时,你需要注意以下几点来实现”找零”:

  1. 确定需要支付的金额,包括交易金额和矿工费用。
  2. 找到足够的未花费的交易输出来支付这个金额。
  3. 将这些交易输出作为交易的输入,并且将它们对应的公钥哈希和签名添加到输入中。
  4. 如果你支付的金额少于交易输入的总金额,那么你需要创建一个新的交易输出来表示”找零”。

以下是一个简单的示例,演示如何实现”找零”。在这个示例中,我们假设有一个输入交易输出了 10 个比特币,我们需要支付 5 个比特币给收件人,并且支付 1 个比特币作为矿工费用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 构建交易输入
TransactionInput input = new TransactionInput(txHash, outputIndex);
input.setSignature(signature);
input.setPublicKey(publicKey);

// 构建交易输出
TransactionOutput output1 = new TransactionOutput(5.0, receiverAddress);
TransactionOutput output2 = new TransactionOutput(4.0, changeAddress);

// 构建交易
Transaction transaction = new Transaction();
transaction.addInput(input);
transaction.addOutput(output1);
transaction.addOutput(output2);

在这个示例中,我们创建了一个输入,表示我们要花费一笔交易中的一个未花费的输出。我们还创建了两个输出,一个用于支付收件人,另一个用于”找零”。在这个示例中,我们假设支付的金额为 5 个比特币,因此我们需要创建一个值为 4 个比特币的交易输出来表示”找零”。

2.4 UTXO 处理时注意事项

除了示例中提到的 UTXO 实现中需要考虑的细节和安全性问题外,以下是一些其他可能需要考虑的方面:

  1. 双重支付攻击(Double-spending attack):在 UTXO 实现中,需要确保同一笔 UTXO 只能被花费一次。否则,攻击者可能会花费同一笔 UTXO 两次,以欺诈商家或其他用户。为了避免这种攻击,可以使用一些技术,如 UTXO 索引和 UTXO 池等。
  2. 确认时间(Confirmation time):在 UTXO 实现中,需要考虑确认时间的问题。确认时间是指交易被其他节点接受和验证的时间。在 Bitcoin 网络中,通常需要等待 6 个区块(约 1 小时)才能确认一个交易。这是为了确保交易不会被双重支付攻击或其他欺诈行为所影响。
  3. 交易费用(Transaction fee):在 UTXO 实现中,需要考虑交易费用的问题。交易费用是指矿工从交易中获得的报酬。矿工通常会优先处理交易费用较高的交易,因此,如果交易费用较低,则可能需要等待更长的时间才能被处理。

2.5.3 Transaction交易类

脚本机制

解锁脚本

解锁脚本(unlock script)是一个脚本,用于解锁指定交易输出(UTXO)并花费它。它应该与输出脚本(lock script)相对应。要生成解锁脚本,您需要知道将要使用的交易输入(input)和相应的输出脚本(lock script)。解锁脚本应该包含足够的信息来验证该交易的所有权,并且可以让其成为区块链中的有效交易。

对于 P2PKH 输出脚本,解锁脚本应该包含以下内容:

1.签名数据:用于证明该交易的所有权。 2.公钥:用于验证签名数据的有效性。

下面是一个示例 P2PKH 解锁脚本生成代码:

1
2
3
4
5
6
7
public static String generateUnlockScript(String signature, String publicKey) {
StringBuilder script = new StringBuilder();
script.append(signature);
script.append(" ");
script.append(publicKey);
return script.toString();
}

其中,signature 是经过签名的数据,publicKey 是用于验证该签名的公钥。生成的解锁脚本应该与输出脚本相匹配,并且应该能够在区块链中成功验证该交易的所有权。需要注意的是,不同的输出脚本可能需要不同类型的解锁脚本来解锁。

2.5.4 确认机制

比特币中验证一个区块的流程,我需要非常详细的流程

比特币中验证一个区块的流程可以分为以下步骤:

  1. 确认区块头哈希值的正确性。每个区块都有一个区块头,其中包含了区块的元数据和交易摘要。每个区块头中包含了先前一个区块的哈希值,如果该哈希值与区块链中该位置上的哈希值相同,则认为该区块头哈希值正确。
  2. 验证所有交易的有效性。该步骤会验证每个交易的数字签名和输入交易的可用性。数字签名验证是用于证明交易发送者对该交易的所有权和可支配性。输入交易可用性验证是指确保每个输入交易在区块链中都存在且未被花费。
  3. 计算该区块的工作证明。比特币使用工作量证明(Proof of Work)算法来生成新的区块。为了证明该区块的有效性,需要计算其哈希值,并验证其是否符合当前的难度目标。如果该区块的哈希值小于当前的难度目标,则认为该区块的工作证明有效。
  4. 确认该区块中的交易在先前的区块中未被包含。这是为了避免“双花”攻击,即同一个比特币被同时发送到不同的地址。
  5. 确认该区块的时间戳合法。该时间戳必须大于前一个区块的时间戳,但不能超过未来一定的时间。
  6. 确认该区块的奖励和交易费用合法。每个区块包含一个新的比特币奖励和所有交易的交易费用。这些奖励和费用必须是合法的,否则该区块将无效。
  7. 将该区块添加到区块链中。如果该区块通过了以上所有验证步骤,就可以将其添加到区块链中,成为最新的区块。

以上是比特币中验证一个区块的大致流程。需要注意的是,该流程可能因比特币协议的更新而有所变化,因此请参考最新的比特币协议文档以获取最新信息。

一个比特币中的节点收到了新的区块并对其通过了验证,那么该节点已打包交易信息需要如何处理,丢弃吗还是继续保持,还是删去在新区块中出现的交易

当一个比特币节点收到新的区块并验证通过后,该节点已经知道了新的交易信息。节点会将这些新的交易信息与自己本地的交易信息进行比较,以确定新区块是否包含了自己还未被确认的交易。如果新区块中确实包含了该节点未被确认的交易,那么该节点会将该交易标记为“已确认”,并将其从节点的未确认交易池中删除。如果新区块中包含的交易已经被该节点确认,则该节点将保持不变。

需要注意的是,如果新区块中包含了该节点未被确认的交易,但该交易的输入交易已经被包含在区块链中并且已经被确认,那么该交易会被视为“双重支付”,该节点会将其标记为无效交易,并将其从节点的未确认交易池中删除。如果新区块中包含的交易与该节点的交易信息不匹配,则该节点会将其标记为“冲突交易”,并在未来的交易中将其优先级降低,以避免发生“双重支付”和其他错误。

总之,一个比特币节点在接收新区块后会对其中的交易信息进行处理,保留或丢弃取决于该节点本地的交易信息和区块链的状态。如果新区块中包含了该节点未被确认的交易,该交易将被标记为“已确认”并从未确认交易池中删除,否则该节点将保持不变。

未确认交易是什么意思,未确认交易池是什么意思,指的是该交易没有被该节点打包为区块吗

是的,未确认交易指的是尚未被打包进区块的交易,也称为未确认交易。当一个比特币交易被广播到网络上时,它进入了各个节点的未确认交易池中,等待被矿工打包进区块并被确认。

未确认交易池是指一个比特币节点存储未确认交易的内存池,每当该节点接收到新的未确认交易时,它将被添加到未确认交易池中。节点会在其本地维护未确认交易池,并将这些交易传播到网络中的其他节点,以便矿工可以挑选其中的交易打包进区块。

节点会定期地清理其未确认交易池,删除已经过期或者无效的交易。当交易被打包进区块并被确认时,它将从未确认交易池中移除,并添加到区块链中,成为一个已确认的交易。因此,未确认交易池是一个临时存储未确认交易的地方,是比特币交易流程中的一个重要组成部分。

打包进区块和被确认不是一码事吗,什么是确认呢

是的,打包进区块和被确认是两个不同的过程。

当一个矿工挖出一个新的区块时,他会将该区块中包含的未确认交易打包进去。这个过程被称为“打包进区块”,或者简称“打包”。被打包进区块的交易会被广播到网络上的其他节点,并在他们的未确认交易池中移除。

但是,一个交易被打包进区块并不意味着它已经被确认。确认是指在区块链上添加了一个新区块,并且这个区块中的交易已经被计算机节点验证和认可。在比特币网络中,一般情况下需要至少6个区块的确认,才能认为一个交易是完全确认的。这是因为,随着新的区块不断被添加到区块链中,之前的区块和其中的交易也会被进一步确认,从而增加整个交易的安全性。

因此,一个交易的确认状态是指该交易已经被计算机节点验证,并被添加到区块链上,被确认的交易不会被修改或撤销。交易的确认状态是比特币网络中的一个重要指标,它可以用来判断交易是否成功完成,并衡量交易的安全性和可靠性。

需要至少6个区块的确认,才能认为一个交易是完全确认的。这句话每台明白,一个区块被确认后不是就被放入区块链中了吗,那后面区块的确认跟前面区块的确认有什么关联吗

非常抱歉,我的回答可能有误导之处。事实上,在比特币网络中,一个交易被确认的次数并没有一个固定的要求,而是取决于交易的价值和风险承受能力。

当一个交易被包含在一个区块中时,它会被认为是“已确认”的,但这并不是最终确认。因为在比特币网络中,区块可以被更长的区块链替换掉,也就是发生了“区块重组”。

当一个区块被挖出来时,它只是暂时的,因为在比特币网络中,可能有其他矿工也在同一时间内挖出了不同的区块,这就会导致区块链的分叉。在这种情况下,比特币网络会选择最长的链作为有效的区块链,而矿工们则会选择挖掘新的区块,以使他们的区块成为最长的链的一部分。

因此,一个交易的确认次数是指该交易所在的区块已经被确认,并且这个区块及其前面的所有区块都没有被替换掉。因为比特币网络要求一个区块链只能有一个有效的链,所以越长的链越难被替换掉,因此交易被确认的次数越多,就越难被撤销或篡改。

总之,一个交易的确认次数是指该交易所在的区块及其前面的所有区块都已经被确认,并且这个区块链没有被替换掉。交易确认次数的要求并没有固定的规定,而是取决于交易的价值和风险承受能力。

也就是说一个区块链,其倒数第六个区块之前所有的区块,可以被认为是完全确认的区块,而刚刚加入的这6个区块,不是完全确认区块对吗

不完全正确。在比特币网络中,一个区块被确认指的是该区块及其前面的所有区块都已经被挖出来并添加到了区块链上。因此,一个区块被确认的次数是指在该区块后面新增的区块数,而不是倒数第六个区块之前所有的区块。

通常来说,一个交易被认为是“完全确认”的需要满足的条件是该交易所在的区块已经被添加到了区块链上,并且在该区块后面新增的区块数已经达到一定的数量。在比特币网络中,通常认为一个交易被确认需要经过6个区块的确认。也就是说,如果一个交易所在的区块已经被添加到了区块链上,并且在该区块后面新增了至少6个区块,那么这个交易就可以被认为是“完全确认”的,即已经非常安全地被确认了。

需要注意的是,虽然6个区块的确认已经足够安全,但这并不是绝对的,因为在极少数情况下,可能会发生区块链分叉的情况,导致之前已经确认的交易被覆盖掉。不过,这种情况非常罕见,可以认为是可以忽略不计的。

完全确认这个概念有什么意义吗,对于用户来说

对于用户来说,“完全确认”这个概念非常重要,因为它决定了用户的交易是否已经最终确认,并且被认为是不可逆转的。当一个交易被确认之后,就意味着该交易所涉及的资产已经成功地转移了,并且不会再被撤销或者更改。因此,用户可以放心地将资产交易给其他人或者使用这些资产进行其他操作。

在比特币网络中,一个交易被认为是“完全确认”的条件是该交易所在的区块已经被添加到了区块链上,并且在该区块后面新增了至少6个区块。因此,当一个交易被确认6次之后,就可以认为该交易已经达到了最终确认的状态。在这种情况下,用户可以非常放心地将资产交易给其他人或者使用这些资产进行其他操作,因为这些交易已经被认为是非常安全和可靠的。

需要注意的是,虽然6次确认已经非常安全,但在一些特殊情况下,可能会发生区块链分叉的情况,导致之前已经确认的交易被覆盖掉。不过,这种情况非常罕见,可以认为是可以忽略不计的。因此,对于绝大多数的用户来说,完全确认这个概念是非常有意义的,因为它可以帮助用户确认他们的交易已经达到了最终的状态,并且不会再被更改或撤销。

那一个用户如何判断一个交易是否被完全确认呢,根据本地的区块链吗

img

是的,一个用户可以通过查看本地的区块链来判断一个交易是否被完全确认。当一个交易被广播到比特币网络中时,每个节点都会将该交易添加到它们的交易池中,并且开始验证该交易的有效性。如果该交易被验证通过,节点将会将该交易打包进一个区块中,然后将该区块广播到整个网络中。当其他节点接收到该区块后,它们也会验证该区块的有效性,并将其添加到它们自己的区块链中。

因此,当一个用户发送一个交易后,可以通过查看比特币网络中是否已经包含该交易所在的区块来判断该交易是否被完全确认。用户可以在比特币区块浏览器中查找该交易的哈希值,并查看该交易所在的区块高度。如果该区块高度后面新增了至少6个区块,就可以认为该交易已经被完全确认了。

需要注意的是,不同的比特币节点可能会存在一定的延迟,因此可能存在一些节点已经包含了该交易的区块,而其他节点还没有。因此,如果用户在区块链浏览器中找不到自己的交易,可以等待一段时间后再次查找,以确保该交易已经被广播到整个比特币网络中。

话说回来,如果一个节点接收到一个新区块,并且这个新区块检验通过,那么该区块中所有包含的交易,应该在本地未确认区块池中删除

img

是的,当一个节点接收到一个新的区块并验证通过后,该节点会将该区块中包含的所有交易添加到它们的区块链中,并且从它们的本地未确认交易池中删除这些交易。

在比特币网络中,每个节点都维护着一个未确认交易池,该池中存储着节点已经接收到但尚未被包含在区块中的交易。当节点接收到一个新区块时,它会首先验证该区块的有效性,然后将该区块中包含的所有交易添加到它们的区块链中,并将其从本地的未确认交易池中删除。

这样做的好处是可以确保比特币网络中的交易能够快速地得到处理和确认,并避免重复验证已经被确认的交易,从而提高比特币网络的整体性能和效率。

那我需要挖矿,是要处理哪些数据呢

如果您需要进行挖矿,您需要处理的数据通常包括:

  1. 交易数据:您需要验证并处理交易数据。比如,在比特币中,您需要验证交易是否符合协议规则,包括交易输入是否来自有效的地址、交易输出是否符合规则等等。
  2. 区块头数据:在比特币中,您需要对区块头数据进行 SHA-256 哈希计算,并寻找符合工作量证明算法规则的哈希值,也就是需要寻找符合协议要求的“难题”的解决方案。区块头数据包括区块版本号、前一区块的哈希、交易数据的哈希、时间戳、难度目标值和随机数(Nonce)。
  3. 共识算法数据:根据不同的共识算法,您需要处理不同的数据。比如,比特币使用的是工作量证明算法,而以太坊使用的是基于Proof of Stake的共识算法,所需要处理的数据也不同。

需要注意的是,挖矿需要大量的计算资源,因此一般需要使用专门的挖矿设备或者在云平台上租用计算资源。在实践中,大多数人选择加入挖矿池,与其他矿工合作,共同挖掘区块并分享收益。

当一个交易发起后,所有节点都会收到这个交易吗

当一个交易发起后,该交易会被广播到整个区块链网络中的每个节点,因此所有节点都会收到这个交易。

当一个节点收到一个交易后,它会验证该交易的合法性。这个过程通常包括检查该交易是否遵守了区块链协议的规则,以及该交易是否包含有效的数字签名。如果该交易被认为是有效的,该节点将把它添加到其未确认交易池中,并将其广播到网络中的其他节点。

当其他节点收到该交易时,它们会执行相同的验证过程,如果该交易合法,它们也会将其添加到它们自己的未确认交易池中,并将其广播到其他节点。

一旦交易被包含在一个新的区块中并被确认,它将被视为不可逆的,并被视为已完成。在比特币中,一笔交易通常需要被包含在至少6个区块中才会被视为确认,而在以太坊中,一笔交易通常需要被包含在至少12个区块中才会被视为确认。

比特币区块链中,一个区块的哈希值是由哪些参数算得的

具体来说,比特币区块链中一个区块的哈希值是由以下参数计算得到的:

  1. 区块版本号(Version):标识区块所使用的协议版本。
  2. 前一个区块的哈希值(Previous Block Hash):指向前一个区块的哈希值,通过这个参数将区块链接在一起,形成区块链。
  3. Merkle 根(Merkle Root):将区块中包含的所有交易记录经过哈希计算得到一个 Merkle 树,再将 Merkle 树的根节点作为 Merkle 根参数。
  4. 时间戳(Timestamp):记录区块被创建的精确时间,以 Unix 时间戳的形式表示。
  5. 难度目标(Bits):表示区块头哈希值的难度目标,通过这个参数可以调整挖矿难度。
  6. 随机数(Nonce):矿工在挖矿过程中需要不断尝试不同的随机数值,使得区块头的哈希值满足难度目标的要求。

这些参数通过哈希函数(通常使用 SHA-256)进行哈希计算,得到的哈希值就是该区块的唯一标识符。需要注意的是,区块中还包含了其他一些参数,如区块高度、交易数量等,但这些参数并不直接参与哈希计算。

公钥的作用

在区块链系统中,公钥作为密码学的基本工具之一,具有以下几个主要的作用:

  1. 数字签名验证:公钥可以用于验证数字签名的有效性。在区块链中,交易和区块都需要经过数字签名验证,以保证其完整性和真实性。发送方使用私钥对交易或区块进行签名,接收方使用对应的公钥来验证签名是否正确。
  2. 身份验证:公钥可以用于身份验证。在区块链中,公钥被用作身份的唯一标识。每个用户都有一对公钥和私钥,公钥可以公开,私钥必须保密。当用户在网络上进行交易时,他们需要用自己的私钥对交易进行签名,并将交易广播到网络中。其他节点通过验证签名来确定交易是否来自合法的用户。
  3. 密钥交换:公钥可以用于密钥交换。在区块链中,公钥可以被用于协商共享密钥,实现安全的通信。例如,在以太坊智能合约中,合约中的函数可以根据公钥生成一个共享密钥,从而保证只有持有对应私钥的用户可以调用该函数。
  4. 地址生成:公钥可以用于生成钱包地址。在比特币和其他加密货币中,钱包地址是由公钥通过一定算法生成的。因此,如果您拥有一个公钥,您就可以通过相应的算法生成一个钱包地址。

总之,公钥在区块链系统中起着重要的作用,保障了系统的安全性和可靠性。

3 区块链实现

3.1 数据结构的定义

3.1.1 区块数据结构

image-20230306190805778

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package com.dce.blockchain.web.entity;

import com.dce.blockchain.web.util.CryptoUtil;
import com.dce.blockchain.web.util.MerkleTree;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.io.Serializable;
import java.util.List;
import java.util.Objects;

/**
* 区块结构
*
* @author Jared Jia
*/

@Setter
@Getter
@Slf4j
public class Block implements Serializable {

private static final long serialVersionUID = 1L;
/**
* 区块索引号(区块高度)
*/
private int index;
/**
* 当前区块的hash值,区块唯一标识
*/
private String hash;

/**
* 默克尔树哈希值
*/
private String merkleHash;
/**
* 前一个区块的hash值
*/
private String previousHash;
/**
* 生成区块的时间戳
*/
private long timestamp;
/**
* 工作量证明,计算正确hash值的次数
*/
private int nonce;
/**
* 当前区块存储的业务数据集合(例如转账交易信息、票据信息、合同信息等)
*/
private List<Transaction> transactions;


public Block(int index, long timestamp, int nonce, String previousHash, List<Transaction> blockTxs) {
this.setIndex(index);
// 时间戳
this.setTimestamp(timestamp);
// 工作量证明,计算正确hash值的次数
this.setNonce(nonce);
// 前区块的哈希
this.setPreviousHash(previousHash);
// 当前区块的哈希
this.setHash(calculateHash());
// MerkleHash
this.setMerkleHash(MerkleTree.getMerkleRoot(blockTxs));
}

/**
* 向该区块中添加一笔交易
*/
public boolean addTransaction(Transaction transaction, BlockChain blockChain) throws IOException {
if (Objects.isNull(transaction)) {
return false;
}
if (!Objects.equals(previousHash, "0") && (!transaction.processTransaction(blockChain))) {
log.error("Transaction failed to process.");
return false;
}
transactions.add(transaction);
log.info("Transaction successfully added to block");
return true;
}


/**
* 验证新区块是否有效,通常是用于新区块验证
*/
public boolean isValidNewBlock(String previousHash) {
if (!previousHash.equals(this.getPreviousHash())) {
log.error("新区块的前一个区块hash验证不通过");
return false;
} else {
// 验证区块hash值的正确性
String curHash = calculateHash();
if (!curHash.equals(this.getHash())) {
log.error("新区块的hash无效: " + curHash + " " + this.getHash());
return false;
}
return isValidHash(this.getHash());
}
}

/**
* 验证hash值是否满足系统条件
*/
public static boolean isValidHash(String hash) {
return hash.startsWith("00000");
}

/**
* 计算区块的hash
*/
public String calculateHash() {
return CryptoUtil.SHA256(previousHash + merkleHash + nonce + timestamp);
}

public static String calculateHash(String previousHash, String merkleHash, int nonce, long timestamp) {
return CryptoUtil.SHA256(previousHash + merkleHash + nonce + timestamp);
}
}

根据之前文章中提到的区块数据结构图,构造出区块的数据结构,详细的属性解释看注释。

3.1.2 区块链数据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package com.dce.blockchain.web.entity;

import com.alibaba.fastjson.JSON;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.java_websocket.WebSocket;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

@ConfigurationProperties(prefix = "block")
@Component
@Getter
@Setter
@Slf4j
public class BlockChain {

/**
* UTXO池,记录所有未花费交易
*/
UTXOPool utxoPool = new UTXOPool();
/**
* 当前节点的区块链结构
*/
private List<Block> blockChain = new CopyOnWriteArrayList<>();

/**
* 已验证未确认交易池
*/
private List<Transaction> packedTransactions = new CopyOnWriteArrayList<>();

/**
* 当前节点的socket对象
*/
private List<WebSocket> socketsList = new CopyOnWriteArrayList<>();
/**
* 挖矿的难度系数
*/
@Value("${block.difficulty}")
private int difficulty;

/**
* 当前节点p2PServer端口号
*/
@Value("${block.p2pport}")
private int p2pPort;

/**
* 要连接的节点地址
*/
@Value("${block.address}")
private String address;

/**
* 获取最新的区块,即当前链上最后一个区块
*
* @return
*/
public Block getLatestBlock() {
return !blockChain.isEmpty() ? blockChain.get(blockChain.size() - 1) : null;
}


/**
* 添加新区块到区块链中
*/
public boolean judgeAndAdd(Block newBlock, String previousHash) {
//先对新区块的合法性进行校验
if (newBlock.isValidNewBlock(previousHash)) {
this.getBlockChain().add(newBlock);
return true;
}
return false;
}


/**
* 验证整个区块链是否有效
*
* @param chain
* @return
*/
public boolean isValidChain(List<Block> chain) {
Block block = null;
Block lastBlock = chain.get(0);
int currentIndex = 1;
while (currentIndex < chain.size()) {
block = chain.get(currentIndex);
if (!block.isValidNewBlock(lastBlock.getHash())) {
return false;
}
lastBlock = block;
currentIndex++;
}
return true;
}

/**
* 替换本地区块链,通常是在一个新节点加入需要获取全部区块链数据时
*
* @param newBlocks
*/
public void replaceChain(List<Block> newBlocks) {
// 本地区块链
List<Block> localBlockChain = this.getBlockChain();
// 本地已打包的交易信息
List<Transaction> localpackedTransactions = this.getPackedTransactions();
// 新区块链有效切长度更长,替换本地区块链
if (isValidChain(newBlocks) && newBlocks.size() > localBlockChain.size()) {
localBlockChain = newBlocks;
// 替换已打包保存的业务数据集合
localpackedTransactions.clear();
localBlockChain.forEach(block -> {
localpackedTransactions.addAll(block.getTransactions());
});
this.setBlockChain(localBlockChain);
this.setPackedTransactions(localpackedTransactions);
log.info("替换后的本节点区块链:" + JSON.toJSONString(this.getBlockChain()));
} else {
log.error("接收的区块链无效");
}
}
}

属性解释:

  • utxoPool:本节点记录的所有未花费支出交易池,可以理解为全部用户没有使用的支票总和
  • blockChain:本节点所保存的全部区块,可以想象为区块链,不过是放在一个List中保存。
  • packedTransactions:已打包交易总和,只有当“挖矿”成功时,才需要将已打包交易放入一个新的区块中并发送给其他节点,如果本节点没有“挖矿”或是“挖矿”不成功,打包的交易永远不会被放入新区块。
  • socketsList:与本节点相连接的Socket,可以想象为P2P网络中直接与本节点相连接的节点。
  • difficulty:挖矿的难度系数。

3.1.3 UTXO池数据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.dce.blockchain.web.entity;

/**
* @program: dce-blockchain
* @description: UTXO池
* @author: CuiJieXiang_1023
* @create: 2023-02-16 21:03
**/
import lombok.Getter;
import java.util.HashMap;

public class UTXOPool {
@Getter
private HashMap<String, TransactionOutput> utxoPool; // UTXO池

// 构造函数
public UTXOPool() {
utxoPool = new HashMap<String, TransactionOutput>();
}

// 添加一个UTXO到UTXO池中
public void addUTXO(String utxo, TransactionOutput txOut) {
utxoPool.put(utxo, txOut);
}

// 从UTXO池中移除一个UTXO
public TransactionOutput removeUTXO(String utxo) {
return utxoPool.remove(utxo);
}

// 根据UTXO获取输出
public TransactionOutput getTxOutput(String utxo) {
return utxoPool.get(utxo);
}

// 获取UTXO池中的所有UTXO
public TransactionOutput[] getAllUTXO() {
return utxoPool.keySet().toArray(new TransactionOutput[utxoPool.size()]);
}
}

3.1.4 Transaction数据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
package com.dce.blockchain.web.entity;


import com.dce.blockchain.web.util.CommonUtil;
import com.dce.blockchain.web.util.CryptoUtil;
import com.dce.blockchain.web.util.ScriptUtils;
import com.dce.blockchain.web.util.WalletAddressGenerator;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.*;

/**
* Created by simon on 2018/2/9.
*/
@Slf4j
@Data
@NoArgsConstructor
public class Transaction {
// 本次交易的唯一id号
public String id;

//也是本次交易的hash值
public String hash;

//双方的钱包地址
public String senderAddress;
public String receiverAddress;

//发送方的公钥,用于验证签名
public PublicKey sender;


public float value;

// 数字签名保证两点:
// 1.只有数字货币的拥有者才能使用。
// 2.在交易被矿工挖出之前(共识前)交易不会被篡改。
public byte[] signature;

// 这笔交易花费
private List<TransactionInput> inputs = new ArrayList<>();

// 这笔交易所得
private List<TransactionOutput> outputs = new ArrayList<>();

//统计交易频次
private static int sequence = 0;

public Transaction(PublicKey from, String receiverAddress, float value) {

this.id = String.valueOf(UUID.randomUUID());
this.sender = from;
this.receiverAddress = receiverAddress;
this.senderAddress = WalletAddressGenerator.generateAddress(from);
this.value = value;
}

/**
* 创建改交易的签名
*
* @param privateKey
*/
public void generateSignature(PrivateKey privateKey) {
// 这里只对交易双方和 value 进行了签名,可以签名更多信息
String data = CryptoUtil.getStringFromKey(sender) + receiverAddress + value;
signature = CryptoUtil.applyECDSASig(privateKey, data);
}

/**
* 验证签名
*
* @return
*/
public boolean verifySignature() {
String data = CryptoUtil.getStringFromKey(sender) + receiverAddress + value;
return CryptoUtil.verifyECDSASig(sender, data, signature);
}


// 处理转账
// 当 sendFunds 输入 input 后调用
public boolean processTransaction(BlockChain blockChain) throws IOException {

// 计算零钱
float leftOver = getInputValue() - value;
// 验证交易的输入与输出
if (leftOver < 0.0) {
// 交易逻辑
log.error("sum of Inputs is smaller than value, sum:" + getInputValue() + ",value:" + value);
return false;
}

// 本次交易的每一笔开销,找到其在UTXO池中的UTXO
for (TransactionInput input : inputs) {
input.transactionOutput = blockChain.utxoPool.getTxOutput(input.getTransactionOutputId());
}

// 判断交易是否合理
if (!isValidTransaction(blockChain)) {
return false;
}
// 修改本地UTXO池
modifyUTXOPool(blockChain);

// 修改区块链中未打包交易
blockChain.getPackedTransactions().add(this);
return true;
}


// 判断本次交易是否合规
public boolean isValidTransaction(com.dce.blockchain.web.entity.BlockChain blockChain) {
// 验证签名
if (!verifySignature()) {
log.error("签名错误");
return false;
}

// 验证公钥是否正确
if (!CommonUtil.isValidBitcoinKey(sender)) {
log.error("发送方公钥验证错误");
return false;
}

// 用 Set 判断是否双花
Set<String> usedTransactionOutputId = new HashSet<>();
// 记录总输入
float totalInput = 0L;
// 判断花费的 UTXO 是否合理
for (TransactionInput input : inputs) {
TransactionOutput txOutput = blockChain.utxoPool.getTxOutput(input.getTransactionOutputId());
// 本次交易中花费的 UTXO 未在本地出现
if (txOutput == null) {
log.error("本次交易中花费的 UTXO 未在本地出现");
return false;
}
// 判断是否双花
if (usedTransactionOutputId.contains(txOutput.id)) {
log.error("交易存在双花,本次交易无效");
return false;
}
usedTransactionOutputId.add(txOutput.id);
// 判断是否能使用解锁脚本解锁上锁脚本
if (!ScriptUtils.isScriptValid(input.unlockScript, txOutput.lockScript)) {
log.error("上锁脚本解锁失败,本次交易无效");
return false;
}
totalInput += txOutput.value;
}

// 判断交易输出是否合理
float totalOutput = 0L;
for (com.dce.blockchain.web.entity.TransactionOutput output : outputs) {
if (output.value <= 0) {
return false;
}
totalOutput += output.value;
}
return !(totalInput < totalOutput);
}


/**
* 操作 BlockChain 修改区块链UTXO池数据
* @param blockChain
*/
public void modifyUTXOPool(BlockChain blockChain) {
// 将所有output加入到hashmap
for (com.dce.blockchain.web.entity.TransactionOutput output : outputs) {
blockChain.utxoPool.addUTXO(output.id, output);
}
hash = calculateHash();
// 将已经被使用了的input从hashmap中删除
for (TransactionInput input : inputs) {
blockChain.utxoPool.removeUTXO(input.getTransactionOutputId());
}
}


// 发起交易者所付出的钱
public float getInputValue() {
float total = 0;
for (com.dce.blockchain.web.entity.TransactionInput input : inputs) {
if (input.transactionOutput == null) {
continue;
}
total += input.transactionOutput.value;
}
return total;
}

// 接收交易者所得到的钱
public float getOutputValue() {
float total = 0;
for (com.dce.blockchain.web.entity.TransactionOutput output : outputs) {
total += output.value;
}
return total;
}

// 本次交易的 Hash值
private String calculateHash() {
sequence++;
return CryptoUtil.applySha256(CryptoUtil.getStringFromKey(sender) + receiverAddress + value + sequence);
}

/**
* 是否与某一个钱包相关
* @param myWallet
* @return
*/
public boolean aboutWallet(Wallet myWallet) {
return this.receiverAddress.equals(myWallet.getWalletAddress()) || this.senderAddress.equals(myWallet.getWalletAddress());
}

/**
* 是否与本地用户相关
* @param user
* @return
*/
public boolean aboutUser(User user) {
for (Wallet wallet : user.getWallet()) {
if (aboutWallet(wallet)) {
return true;
}
}
return false;
}
}

3.1.5 TransactionInput 数据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.dce.blockchain.web.entity;

import com.dce.blockchain.web.util.ScriptUtils;
import lombok.Getter;

import java.security.PublicKey;

/**
* Created by simon on 2018/2/9.
*/
@Getter
public class TransactionInput {

// 每一个输入都对应着一笔交易的输出,沿用比特币白皮书中的描述,将其命名为UTXO
private String transactionOutputId;

public TransactionOutput transactionOutput;

// 解锁脚本
public String unlockScript;

/**
*
* @param transactionOutputId
* @param signature 所属交易的数字签名,用于生成解锁脚本
* @param publicKey 交易发起人的公钥
*/
public TransactionInput(String transactionOutputId, byte[] signature, PublicKey publicKey) {
this.transactionOutputId = transactionOutputId;


this.unlockScript = ScriptUtils.generateUnlockScript(signature, publicKey);
}
}

3.1.6 TransactionOutput 数据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.dce.blockchain.web.entity;

import com.dce.blockchain.web.util.ScriptUtils;

import java.io.IOException;

/**
* UTXO类,记录还诶有花出去的收入
*/
public class TransactionOutput {

// output id号
public String id;

// 接收方公钥哈希
public String receiverPublicAddress;

// output金额
public float value;

// 所属交易
public String parentTransactionId;

// 上锁脚本
public String lockScript;


public TransactionOutput(String receiverPublicAddress, float value, String parentTransactionId) throws IOException {
this.receiverPublicAddress = receiverPublicAddress;
this.value = value;
this.parentTransactionId = parentTransactionId;
this.lockScript = ScriptUtils.generateLockScript(receiverPublicAddress);
}

/**
* 传入一个钱包,判断本次交易输出是否属于该钱包
*
* @param wallet
*/
public boolean isBelongToWallet(Wallet wallet) {
return wallet.getWalletAddress().equals(receiverPublicAddress);
}
}

3.1.7 用户数据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package com.dce.blockchain.web.entity;

import com.dce.blockchain.web.exception.BlockChainException;
import com.dce.blockchain.web.exception.MyExceptionType;
import lombok.Data;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

/**
* @program: dce-blockchain
* @description: 用户信息类
* @author: CuiJieXiang_1023
* @create: 2023-02-18 22:01
**/
@Component
@Data
public class User {
// 用户钱包信息
List<Wallet> userWallets;

// 用户IP信息
String ip;

User() {
userWallets = new ArrayList<>();
}

public List<Wallet> getWallet() {
if (userWallets.isEmpty()) {
// 用户没有钱包
throw new BlockChainException(MyExceptionType.SIGNATURE_NOT_MATCH);
}
return userWallets;
}

/**
* 根据钱包地址获取钱包
*
* @param address
* @return
*/
public Wallet getWalletByAddress(String address) {
Wallet wallet = null;
for (Wallet wallet1 : userWallets) {
if (wallet1.getWalletAddress().equals(address)) {
wallet = wallet1;
break;
}
}
if (wallet == null) {
throw new BlockChainException(MyExceptionType.WALLET_NOT_MATCH_ADDRESS);
}
return wallet;
}
}

3.1.8 钱包数据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package com.dce.blockchain.web.entity;

import com.dce.blockchain.web.util.KeyUtils;
import com.dce.blockchain.web.util.WalletAddressGenerator;
import lombok.Getter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;
import java.util.Map;

/**
* Created by simon on 2018/2/8.
*/
@Slf4j
@ToString
@Getter
public class Wallet {
// 钥匙
private PrivateKey privateKey;
private PublicKey publicKey;

private String walletAddress;

// 区块链
@Autowired
BlockChain blockChain;

public Wallet() {
generateKeyPair();
walletAddress = WalletAddressGenerator.generateAddress(publicKey);
}

/**
* 使用ECC(椭圆曲线算法)生成公私钥
*/
private void generateKeyPair() {
KeyPair keyPair = KeyUtils.generateKeyPair();
privateKey = keyPair.getPrivate();
publicKey = keyPair.getPublic();

// Print the keys as strings
String publicKeyString = Base64.getEncoder().encodeToString(publicKey.getEncoded());
log.info("Public Key: " + publicKeyString);

String privateKeyString = Base64.getEncoder().encodeToString(privateKey.getEncoded());
log.info("Private Key: " + privateKeyString);
}

/**
* 根据本地的UTXO池计算余额
*/
public float getBalance() {
float total = 0;
for (Map.Entry<String, TransactionOutput> item : blockChain.getUtxoPool().getUtxoPool().entrySet()) {
TransactionOutput UTXO = item.getValue();
if (UTXO.isBelongToWallet(this)) {
total += UTXO.value;
}
}
return total;
}


/**
* 清空数据
*/
public void clear() {
this.publicKey = null;
this.privateKey = null;
}
}

3.2 钱包与用户秘钥的实现

3.3 节点通信的实现

3.3 创造创世区块的实现

3.4 PoW功能的实现

3.5 交易功能的实现

3.6 交易验证功能

3.7 区块验证功能

3.8

4 常见问题

4.1 毕业设计的流程

  1. 选题阶段

    在这个阶段,你需要确定你的研究方向和主题。选择合适的研究方向和主题将使你的研究更具实际应用价值和科学研究价值。

  2. 需求分析与现状调查阶段

    在这个阶段,你需要进行市场调查和现状分析,以了解目前区块链在金融领域的应用情况,分析现有的问题和需求。这些分析和调查将为你的研究提供基础数据和关键信息。

  3. 可行性分析阶段

    在这个阶段,你需要评估你的研究方案是否可行。这包括评估技术可行性、商业可行性、法律可行性等方面。通过可行性分析,你可以更好地评估你的研究的可行性和可行性。

  4. 系统设计阶段

    在这个阶段,你需要确定你的系统架构和技术方案。你需要设计和实现一个功能完整、安全可靠的区块链系统,涉及到区块链技术的多个方面,包括共识机制、存储结构、智能合约、隐私保护等。

  5. 开发实现阶段

    在这个阶段,你需要根据你的设计方案进行系统开发和实现。你需要使用你选择的技术和工具进行开发,将你的系统实现为一个可用的软件产品。

  6. 测试与优化阶段

    在这个阶段,你需要进行系统测试和优化。你需要测试你的系统的各个方面,包括功能、性能、安全性等,并对其进行优化和改进,以确保其稳定性和可靠性。

4.2 毕业论文内容

您的毕业论文主要涉及以下内容:

  1. 区块链技术的基本概念和原理。这部分内容将介绍区块链的基本构成和原理,包括区块、哈希、共识机制等概念,以及区块链的去中心化特点和优势。
  2. 金融领域中可信系统的研究现状和传统金融系统问题。这部分内容将介绍金融领域中的可信系统,包括传统的金融交易系统和互联网金融平台,以及它们存在的问题和挑战,如信息不对称、信用风险、数据隐私等方面的问题。
  3. 区块链技术在金融领域的应用场景。这部分内容将介绍区块链技术在金融领域中的应用场景,如数字货币、智能合约、供应链金融、股票交易等方面的应用,以及它们解决的问题和优势。
  4. 区块链技术在金融领域中可信系统的设计与实现。这部分内容将介绍如何利用区块链技术设计和实现可信的金融系统,包括可信数据的存储和传输、交易的验证和确认、智能合约的编写和执行等方面的问题。
  5. 区块链技术在金融领域的应用案例和评估。这部分内容将介绍一些区块链技术在金融领域中的应用案例,如比特币、以太坊、Ripple等项目,以及对它们的技术评估和商业价值分析。

通过对以上内容的研究,您可以深入理解区块链技术在金融领域中的应用和价值,为未来的金融行业发展提供有益的借鉴和启示。