【区块链用例】练习总结

【区块链用例】练习总结

目录

  1. 说明
  2. 区块链简介
  3. 核心特性
  4. 工作原理

0. 说明

本文在Jeiwan的区块链开源项目的基础上进行简单总结。

该项目共分为7个部分,逐步实现了区块链的构成,并最终实现了一个区块链用例。

代码对应的详细讲解为:https://github.com/practical-tutorials/project-based-learning?tab=readme-ov-file#go

Alt

图1 代码讲解位置

需要提醒的是,当我在使用原代码中的代码对钱包文件进行读取时,遇到了这个错误:

gob: type elliptic.p256Curve has no exported fields

这是由于crypto/elliptic包中的 elliptic.p256Curve类型没有导出的字段,而Gob编码器要求结构体中的所有字段必须是导出的。

此时可以通过降低Go版本来解决问题,即,当Go的版本为1.18.10或更低的版本时,代码可以成功执行。

也可以通过修改代码解决,修改后的代码在https://github.com/Gezelligheid1010/Blockchain-exercise/blob/main/wallets.go

具体来说,我定义了一个可导出的类型SerializableWallet来存储椭圆曲线的参数,

type SerializableWallet struct {
	D         *big.Int
	X, Y      *big.Int
	PublicKey []byte
}

并修改了LoadFromFileSaveToFile方法中的解码和编码部分。

// LoadFromFile loads wallets from the file
func (ws *Wallets) LoadFromFile(nodeID string) error {

	walletFile := fmt.Sprintf(walletFile, nodeID)
	if _, err := os.Stat(walletFile); os.IsNotExist(err) {
		return err
	}

	fileContent, err := ioutil.ReadFile(walletFile)
	if err != nil {
		log.Panic(err)
	}

	var wallets map[string]SerializableWallet
	gob.Register(SerializableWallet{})
	decoder := gob.NewDecoder(bytes.NewReader(fileContent))
	err = decoder.Decode(&wallets)
	if err != nil {
		log.Panic(err)
	}
	ws.Wallets = make(map[string]*Wallet)
	for k, v := range wallets {
		ws.Wallets[k] = &Wallet{
			PrivateKey: ecdsa.PrivateKey{
				PublicKey: ecdsa.PublicKey{
					Curve: elliptic.P256(),
					X:     v.X,
					Y:     v.Y,
				},
				D: v.D,
			},
			PublicKey: v.PublicKey,
		}
	}

	return nil
}

// SaveToFile saves wallets to a file
func (ws Wallets) SaveToFile(nodeID string) {
	var content bytes.Buffer
	walletFile := fmt.Sprintf(walletFile, nodeID)

	gob.Register(SerializableWallet{})

	wallets := make(map[string]SerializableWallet)
	for k, v := range ws.Wallets {
		wallets[k] = SerializableWallet{
			D:         v.PrivateKey.D,
			X:         v.PrivateKey.PublicKey.X,
			Y:         v.PrivateKey.PublicKey.Y,
			PublicKey: v.PublicKey,
		}
	}

	encoder := gob.NewEncoder(&content)
	err := encoder.Encode(wallets)
	if err != nil {
		log.Panic(err)
	}

	err = os.WriteFile(walletFile, content.Bytes(), 0644)
	if err != nil {
		log.Panic(err)
	}

}

1. 区块链简介

   区块链(Blockchain)是一种去中心化的分布式账本技术,最初作为比特币的底层技术而被广泛关注。区块链的核心概念是将一系列的交易数据打包成区块(Block),并利用加密技术串联成链(Chain),确保数据的安全性和不可篡改性。
Alt

图2 区块链网络示例

  在区块链网络中,节点是网络中运行的设备或实体,负责处理交易和维护账本。常见的节点类型有三种:全节点、轻节点和矿工节点。全节点存储完整的区块链数据,并验证所有交易;轻节点只存储部分数据,用于查询和发送交易;而矿工节点则需要挖掘新的区块并以此来获得奖励。
  在Jeiwan的区块链用例中,为简化流程,轻节点也存储了完整的区块链数据。


2. 核心特性

区块链的核心特性主要体现在以下几个方面:

2.1. 去中心化(Decentralization)

   在区块链系统中,去中心化意味着数据的存储、验证和管理是由网络中的所有节点共同完成。为了理解数据是如何由所有节点共同维护的,我们可以从以下几个方面来解释:

  • 分布式账本
    区块链的核心是分布式账本,该账本记录了所有的交易数据。区块链上的每个节点都拥有账本的完整副本。当一笔新交易发生时,它会被广播到网络中的所有节点。
    这显而易见会造成数据的冗余,因此代码中使用了Merkle树的优化机制,使节点无需下载整个区块即可验证某笔交易的成员身份。
  • 交易验证
    当一笔交易发起并广播到网络中时,区块链中的节点会独立地对这笔交易进行验证,例如检查发送方的账户中是否有足够的余额完成交易。
  • 共识机制
    区块链的共识机制是去中心化系统中确保数据一致性的核心。共识机制允许分布式网络中的所有节点就数据的有效性达成一致,从而决定哪些交易可以被记录在区块链上。
    代码中使用工作量证明(PoW)的方式来实现共识机制,它需要通过计算找到一个满足特定条件的哈希值,这些计算确保了区块的生成需要经过大量计算,从而保障了区块链的安全性和数据一致性。
  • 数据同步
    当一个节点添加了新区块后,它会将该区块的信息广播给其他节点。其他节点接收到区块信息后,会验证该区块的合法性,验证通过后,区块被添加到本地的区块链副本中。
    如果某个节点的区块链出现错误或篡改,其他节点会拒绝接受该节点的数据,从而保证整个网络的数据完整性。
  • 自我修复能力
    即使某个节点的数据遭到篡改,其他节点的正确数据也会通过共识机制纠正错误节点的数据。

2.2.不可篡改性(Immutability)

   区块链的不可篡改性是通过其链式结构和加密技术实现的。每个区块都包含前一个区块的哈希值,如果要修改一个区块的数据,必须同时修改该区块之后的所有区块的哈希值,这种机制使得篡改区块链上的数据非常困难。

2.3. 透明性(Transparency)

   区块链的透明性是指所有交易记录对所有参与者都是公开的,这使得区块链特别适合于多方协作、对环境中的应用更加信任。虽然区块链是透明的,但它不会公开用户的隐私信息,而是通过使用加密技术和匿名地址来保护用户的身份和隐私。

2.4. 安全性(Security)

   区块链通过多层加密和去中心化机制,确保了数据的高度安全性。 数字签名是区块链技术中确保安全性的一项核心机制。它通过加密算法为交易数据提供了验证身份、保护数据完整性和防止篡改的强大手段。数字签名基于公钥加密技术,它的生成和验证过程如图3所示。
Alt

图3 数字签名生成与验证

为了便于理解,本文引入了一部分代码进行讲解。

节点账户加载代码:

ws.Wallets[k] = &Wallet{
		PrivateKey: ecdsa.PrivateKey{
			PublicKey: ecdsa.PublicKey{
				Curve: elliptic.P256(),
				X:     v.X,
				Y:     v.Y,
			},
			D: v.D,
		},
		PublicKey: v.PublicKey,
}

数字签名生成代码:

r, s, err := ecdsa.Sign(rand.Reader, &privKey, []byte(dataToSign))
signature := append(r.Bytes(), s.Bytes()...)

   其中,参数rand.Reader提供了加密级别的随机数,用于生成签名时所需的随机数k,确保每次签名都不同,增强安全性;&privKey是一个指向ecdsa.PrivateKey 结构的指针,包含私钥D和对应的公钥PublicKey[]byte(dataToSign)是需要签名的数据。
   返回值rs是签名的两个组成部分。
   r是通过将生成的椭圆曲线点的x坐标对椭圆曲线的阶取模得到的。它通常表示为r = (k * G).x mod n,其中k是签名生成过程中使用的随机数,G是椭圆曲线的基点,n是椭圆曲线的阶。
   s是通过私钥D、数据e、以及随机数k计算得到的。计算公式为s = k^(-1) * (e + D * r) mod n。这里,k^(-1)k的模逆。

验证代码:

curve := elliptic.P256()
dataToVerify := fmt.Sprintf("%x\n", txCopy)
rawPubKey := ecdsa.PublicKey{curve, &x, &y}
if ecdsa.Verify(&rawPubKey, []byte(dataToVerify), &r, &s) == false {
	return false
}

   代码首先将交易数据副本转换为哈希值dataToVerify,然后使用从交易的公钥中提取的xy坐标创建ecdsa.PublicKey,最后使用ecdsa.Verify方法验证签名是否有效。
   ecdsa签名验证的核心思想是使用公钥来检查签名是否匹配给定的数据。具体来说,ecdsa.Verify函数会重新计算一个签名值,并将其与提供的rs值进行比较。如果重新计算的签名值与提供的签名值一致,则说明签名是有效的,数据的确由与公钥对应的私钥签名。

2.5. 可追溯性(Traceability)

   区块链的可追溯性依赖于其独特的链式结构以及区块间输入输出的紧密连接。每一个区块不仅包含了自己的数据,还与前一个区块的输出直接关联,这使得区块链具备了高度的可追溯性。
在区块链中,每个区块通常包含多个交易,这些交易记录了数字资产的转移,每笔交易可以被简单地理解为一组输入(inputs)和输出(outputs):

  • 输入:当一笔交易发生时,它会引用之前某笔交易的输出,作为此次交易的输入。例如,在比特币交易中,输入通常是未花费的交易输出(UTXO),代表着之前交易中接收到但尚未使用的比特币。
  • 输出:输出作为此次交易的结果,通常包括接收方将获得的资产(如比特币的金额)以及可能的找零(如果有多余的部分)。这些输出会在之后的交易中作为新的输入被引用。
    为了更好理解,我们可以引入一个例子来进行说明。
    假设我们有一个简单的交易系统,使用区块链记录不同人的交易情况。参与交易的有三个人:A、B、C。区块链交易图示如图4所示。
    交易1:当A给B发送10个代币,这是区块链上的第一笔交易,生成了区块链1。因为它是第一个区块,所以这笔交易没有前一个区块作为输入,并且我们假设A的账户中已经有了10个代币。
    交易2:B现在想把他收到的10个代币中的5个发送给C,就生成了区块2。这笔交易成功的前提是,区块链能够验证B的输入(他在区块1中的10个代币)确实存在且没有被使用过。区块2的输入是区块1的输出,这就形成了两个区块之间的连接。
    交易3:C想把他收到的5个代币中的2个发送给A,这就生成了区块3。同样,区块3的输入来自于区块2的输出,区块3和区块2之间就建立起了连接。
    Alt
图4 区块链交易图示

3. 工作原理

区块链的工作原理可以分为几个关键步骤,如图5所示。
Alt

图5 区块链工作步骤

3.1. 交易发起

   一切从用户发起交易开始。假设用户A想要将一定数量的代币发送给B。他在自己的区块链客户端(去钱包应用)中创建一笔交易,该交易包括发送者的地址、接收者的地址、交易金额、以及一个时间戳。这笔交易还会通过私钥生成一个数字签名。A使用他的私钥对交易信息进行签名,生成的签名与交易信息一起被广播到区块链网络。其他节点可以通过A的公钥验证该签名,以确保交易确实是由A发起的,且未被篡改。

3.2. 交易广播到网络中的节点

   A发起的交易被广播到整个区块链网络,网络中的每个节点都会接收到这笔交易信息。广播过程通常使用P2P(点对点)网络结构,确保交易信息能够迅速传达到所有节点。

3.3. 节点验证交易有效性

   在接收到交易后,每个节点都会验证该交易的有效性。包括余额检查、数字签名验证以及双花检测等,只有通过这些验证的交易才会被认为是有效的。

3.4. 交易进入内存池

   验证通过的交易将被放入内存池,这是一个临时存储区域,等待被矿工打包进新区块。内存池中的交易会按照一定的规则排列,通常矿工会优先选择交易费用较高的交易进行打包。

3.5. 矿工从内存池中选择交易

   矿工(通常是参与区块链网络并使用大量计算能力的节点)从内存池中选择若干交易,并将这些交易打包成一个新的区块。矿工在打包交易时,会计算该区块的候选区块哈希值。

3.6. 矿工竞争区块打包权

   矿工们通过解决复杂的数学难题(通常是工作量证明,即PoW)来竞争区块的打包权。PoW 要求矿工找到一个使得区块哈希值满足一定条件的随机数。这个过程需要大量计算能力,只有第一个找到有效哈希值的矿工才能获得新区块的打包权,并获得相应的区块奖励。

3.7. 成功打包的区块广播到网络

   找到有效哈希值并成功打包区块的矿工会将这个新区块广播到整个区块链网络。所有节点接收到这个新区块后,会进行进一步的验证。

3.8. 网络验证区块

   每个节点在接收到新区块后,会检验区块中包含的所有交易是否有效、区块哈希是否符合网络共识规则(如是否PoW的条件),并且会确认该区块是否应该被添加到区块链中,例如,它是否与当前最长的链相连。

3.9. 新区块被添加到区块链

   通过验证后,新的区块被添加到区块链的末端,成为新的区块。此时区块链上所有节点的链条都应该保持一致。

3.9. 交易完成,记录不可篡改

   一旦新区块被添加到区块链上,区块中的所有交易就被认为是最终确认过的。而当更多区块被添加进区块链时,这些交易会变得越来越难以被篡改,因为篡改意味着必须重新计算其后的所有区块的哈希值,这在实际操作中几乎是不可能的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值