谈谈区块链的基础(一般面试中可能问到)

1、算法和数据结构

1、哈希函数(Hash Functions): 用于生成数据的唯一标识,常用的哈希函数有SHA-256(在比特币中使用)和Keccak-256(在以太坊中使用)。

在Keccak-256的例子如下:

在以太坊中,合约地址通常是由创建合约的交易的发送者地址(也称为创建者地址)和创建者地址的随机数(称为随机数nonce)经过 Keccak-256 哈希运算得到的

下述是一个合约地址生成的例子(该地址计算方式并不是以太坊的唯一方式)

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract AddressCalculator {
    
    
    address public contractCreator;
    address public contractAddress;
    constructor() {
    
    
        // 记录创建合约的地址
        contractCreator = msg.sender;
        // 计算合约地址
        contractAddress = address(uint160(uint(keccak256(abi.encodePacked(contractCreator, block.number)))));
    }
}

在上述例子中,合约的构造函数中计算了合约地址。contractCreator 记录了创建合约的地址,然后通过对 contractCreator 地址和当前区块号(block.number)进行 Keccak-256 哈希运算,得到的结果转换为 address 类型,最终得到了合约的地址

2、梅克尔树(Merkle Trees): 用于将大量数据块的哈希值聚合成一个根哈希,以提高数据的完整性验证效率。

3、共识算法:

a、工作量证明(Proof of Work,PoW): 比特币使用的共识算法,需要通过解决一个数学难题来创建新块(这个没什么说的,大家都明白)。

b、权益证明(Proof of Stake,PoS):权益证明(Proof of Stake,简称PoS)是一种共识机制,其中参与者根据其拥有的代币数量来获得创建新区块的机会。相对于工作量证明(PoW),PoS更注重参与者的经济投入而非计算能力。	
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract CasperFFG {
    
    
    address public currentValidator;
    mapping(address => uint256) public stake;

    // 用户进行质押
    function deposit(uint256 amount) public {
    
    
        require(amount > 0, "Amount must be greater than 0");
        stake[msg.sender] += amount;
    }

    // 用户撤销质押
    function withdraw(uint256 amount) public {
    
    
        require(amount > 0 && amount <= stake[msg.sender], "Invalid withdrawal amount");
        stake[msg.sender] -= amount;
    }

    // 共识机制选择验证者创建新区块
    function selectValidator() public {
    
    
        // 这里简化了条件,实际上可能涉及到更复杂的条件和随机性
        if (createBlock()) {
    
    
            currentValidator = msg.sender;
        }
    }

    // 验证者提交区块
    function createBlock() public view returns (bool) {
    
    
        // 这里简化了条件,实际上可能涉及到更复杂的条件和随机性
        return stake[msg.sender] >= 100;  // 举例:要求至少质押 100 个代币
    }
}

在上述例子中,selectValidator 函数模拟了共识机制的过程,它调用了 createBlock 函数来检查当前验证者是否满足创建新区块的条件。
如果 createBlock 返回 true,则当前调用者(msg.sender)被选为验证者,负责创建新的区块。

c、权益证明+工作量证明(Proof of Stake + Proof of Work,PoS + PoW): 一些区块链系统将PoW和PoS结合,如Decred。以太坊 2.0,
   也称为 Ethereum 2.0 或 Eth2 是该方式的一个例子,在 Eth2 中,PoW 主要用于启动网络和生成初始验证者集合。初始验证者集合是由
   通过 PoW 提交了足够的质押(stake)的用户组成的。一旦初始验证者集合形成,系统就会从中选取验证者执行 PoS。
d、权益证明+工作量证明+时间(Proof of Stake + Proof of Work + Time,PoS + PoW + Time): 例如,Tendermint采用这种方式。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract HybridConsensus {
    
    
    address public currentValidator;
    uint256 public difficulty;
    uint256 public lastBlockTime;

    constructor(uint256 initialDifficulty) {
    
    
        difficulty = initialDifficulty;
        lastBlockTime = block.timestamp;
    }

    // PoW 阶段,初始区块生成
    function generateBlockPoW() public {
    
    
        require(block.timestamp - lastBlockTime > 10 minutes, "Wait for at least 10 minutes between blocks");

        // 执行 PoW 相关逻辑,生成新区块
        // 这里简化了难度调整的逻辑
        require(uint256(blockhash(block.number - 1)) < 2**256 / difficulty, "PoW failed");

        // 切换到 PoS 阶段
        lastBlockTime = block.timestamp;
        difficulty = adjustDifficulty();
        currentValidator = msg.sender;
    }

    // PoS 阶段,验证者确认交易和生成新区块
    function generateBlockPoS() public {
    
    
        require(msg.sender == currentValidator, "Only the current validator can generate a PoS block");

        // 执行 PoS 相关逻辑,确认交易和生成新区块
        // ...

        // 更新当前验证者
        currentValidator = selectValidator();
    }

    // 模拟 PoS 阶段的验证者选取
    function selectValidator() internal view returns (address) {
    
    
        // 这里简化了验证者选取的逻辑,实际应用中可能包含更复杂的机制
        return address(uint160(uint(keccak256(abi.encodePacked(blockhash(block.number - 1), block.timestamp)))) % 10);
    }

    // 模拟难度调整逻辑
    function adjustDifficulty() internal view returns (uint256) {
    
    
        // 这里简化了难度调整的逻辑,实际应用中可能包含更复杂的机制
        if (block.timestamp - lastBlockTime > 15 minutes) {
    
    
            return difficulty / 2;  // 难度减半
        } else {
    
    
            return difficulty * 2;  // 难度翻倍
        }
    }
}

在上述简化的示例中,generateBlockPoW 函数模拟了 PoW 阶段,要求在两个区块之间至少经过 10 分钟的时间,然后通过检查新区块的哈希是否小于当前难度的目标值来执行 PoW。一旦 PoW 阶段成功,就切换到 PoS 阶段,更新验证者、调整难度,并执行 PoS 阶段的逻辑。

4、区块结构:

区块头(Block Header): 存储区块的元信息,包括先前块的哈希、时间戳、难度目标等。

具体内容如下:

		版本号(Version): 表示区块结构的版本。
		前一个区块哈希(Previous Block Hash): 指向前一个区块的哈希值,构成了区块链的链接。
		默克尔根(Merkle Root): 该区块中包含的所有交易的 Merkle Tree 根的哈希。
		时间戳(Timestamp): 区块生成的时间戳。
		难度目标(Difficulty Target): 该区块的挖矿目标,通常是一个表示难度的数值。
		随机数(Nonce): 用于挖矿的随机数,当这个数值能够使区块头哈希满足难度目标时,挖矿成功。
		交易数量(Number of Transactions): 该区块包含的交易数量

交易列表: 记录该区块中的所有交易。

下面是一个区块的例子:

{
    
    
  "header": {
    
    
    "version": 1,
    "previous_block_hash": "0x123456...",
    "merkle_root": "0xabcdef...",
    "timestamp": 1632340000,
    "difficulty_target": "0x0000000...",
    "nonce": 12345,
    "num_transactions": 3
  },
  "transactions": [
    {
    
    
      "from": "0xabc...",
      "to": "0xdef...",
      "amount": 10
    },
  ]
}

5、默克尔帕特里夏树(Merkle Patricia Tree): 以太坊中用于存储账户状态和交易历史的数据结构。

现在来说明一下非对称加密

其是使用一对公私钥进行加密和解密,常见的算法有RSA和椭圆曲线密码学(ECC),其核心概念是使用一对密钥:公钥和私钥。这一对密钥是由一种数学算法生成的,并且它们之间存在特殊的数学关系。其中一个密钥可以用于加密,而另一个用于解密。重要的是,无法从一个密钥推导出另一个密钥,这提供了非对称加密的安全性。*

下面简单解释一下RSA:

密钥生成:
选择两个大素数 p 和 q。
计算 n = p * q。
计算欧拉函数* φ(n) = (p-1) * (q-1)。
选择一个与 φ(n) 互质的整数 e 作为公钥的指数。
计算 d,使得 (d * e) % φ(n) = 1,d 作为私钥的指数。**
(n, e) 构成公钥,(n, d) 构成私钥。

加密:
将明文 M 转换成整数 m。
计算密文 C = m^e mod n。
C 为加密后的密文。

解密:
计算明文 M = C^d mod n。
M 为解密后的明文。
这个文字描述基本展示了RSA算法的流程,其中 n、e 为公钥,n、d 为私钥。在实际应用中,p、q、φ(n)等参数会更大,以增强算法的安全性。

ECC:

在这里插入图片描述

import hashlib
from ecdsa import SigningKey, VerifyingKey, SECP256k1

# 1. 生成密钥对
private_key = SigningKey.generate(curve=SECP256k1)
public_key = private_key.get_verifying_key()

# 2. 准备要签名的消息
message = b"Hello, ECDSA!"

# 3. 计算消息的哈希值
message_hash = hashlib.sha256(message).digest()

# 4. 生成签名
signature = private_key.sign(message_hash)

# 5. 验证签名
try:
    public_key.verify(signature, message_hash)
    print("Signature is valid.")
except ecdsa.BadSignatureError:
    print("Signature is invalid.")

上述代码中,我们使用 ecdsa 库生成了一个新的密钥对,然后准备了一条消息并计算了其SHA-256哈希值。接着,我们使用私钥对消息哈希进行签名,并尝试使用公钥验证签名。如果签名有效,将输出 “Signature is valid.”。

这里使用的椭圆曲线是 SECP256k1,这是比特币等加密货币中广泛使用的曲线。

ECDSA(椭圆曲线数字签名算法)

在这里插入图片描述

import hashlib
from ecdsa import SigningKey, VerifyingKey, SECP256k1

# 1. 生成密钥对
private_key = SigningKey.generate(curve=SECP256k1)
public_key = private_key.get_verifying_key()

# 2. 准备要签名的消息
message = b"Hello, ECDSA!"

# 3. 计算消息的哈希值
message_hash = hashlib.sha256(message).digest()

# 4. 生成签名
signature = private_key.sign(message_hash)

# 5. 验证签名
try:
    public_key.verify(signature, message_hash)
    print("Signature is valid.")
except ecdsa.BadSignatureError:
    print("Signature is invalid.")

在这个示例中,首先生成了一个 ECDSA 密钥对,然后准备了一条消息并计算了其 SHA-256 哈希值。接着使用私钥对消息哈希进行签名,并尝试使用公钥验证签名。如果签名有效,输出 “Signature is valid.”。

请注意,示例中使用的椭圆曲线是 SECP256k1,这是比特币等加密货币中常用的曲线。

AES(高级加密标准)

AES(Advanced Encryption Standard)是一种对称加密算法,广泛用于保护敏感信息的安全性。它是一种替代 DES(Data Encryption Standard)的标准加密算法,目前被广泛接受和使用。

AES 算法支持多种密钥长度,包括 128 比特、192 比特和 256 比特。密钥越长,理论上加密的安全性越高,但也需要更多的计算资源。

下面是 AES 的基本操作步骤:
轮密钥生成:

根据初始密钥生成轮密钥,每一轮使用不同的子密钥。

初始轮(Initial Round):

将明文和第一轮的子密钥进行异或运算。

多轮(Rounds):

从第二轮开始,进行多轮的操作,每一轮包括以下步骤:
	字节替代(SubBytes): 使用 S-盒(Substitution Box)替换每个字节。
	行移位(ShiftRows): 每一行进行循环左移,偏移量取决于行数。
	列混淆(MixColumns): 列与固定矩阵进行混淆。
	轮密钥加(AddRoundKey): 将当前轮的子密钥与状态矩阵进行异或运算。

最终轮(Final Round):

最后一轮省略列混淆步骤。

密文生成:

最后一轮操作后,得到的状态矩阵即为加密后的密文。

AES 提供了高度的安全性和性能,适用于许多安全通信和数据存储场景。在使用时,注意选择适当长度的密钥,并确保密钥的安全存储和传输

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import padding

def encrypt_aes(plaintext, key):
    # 选择 AES 算法和 CBC 模式
    cipher = Cipher(algorithms.AES(key), modes.CBC(bytes.fromhex("00000000000000000000000000000000")), backend=default_backend())
    
    # 创建加密器
    encryptor = cipher.encryptor()
    
    # 使用 PKCS7 填充
    padder = padding.PKCS7(algorithms.AES.block_size).padder()
    padded_data = padder.update(plaintext) + padder.finalize()
    
    # 加密数据
    ciphertext = encryptor.update(padded_data) + encryptor.finalize()
    
    return ciphertext

def decrypt_aes(ciphertext, key):
    # 选择 AES 算法和 CBC 模式
    cipher = Cipher(algorithms.AES(key), modes.CBC(bytes.fromhex("00000000000000000000000000000000")), backend=default_backend())
    
    # 创建解密器
    decryptor = cipher.decryptor()
    
    # 解密数据
    decrypted_data = decryptor.update(ciphertext) + decryptor.finalize()
    
    # 使用 PKCS7 反填充
    unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
    plaintext = unpadder.update(decrypted_data) + unpadder.finalize()
    
    return plaintext

# 示例
key = b"0123456789ABCDEF0123456789ABCDEF"  # 256-bit key
plaintext = b"Hello, AES!"

# 加密
ciphertext = encrypt_aes(plaintext, key)
print(f"Ciphertext: {
      
      ciphertext.hex()}")

# 解密
decrypted_text = decrypt_aes(ciphertext, key)
print(f"Decrypted Text: {
      
      decrypted_text.decode('utf-8')}")

这里使用了 CBC 模式和 PKCS7 填充。在实际应用中,密钥的生成、存储和传递等方面需要特别小心。确保密钥的安全性对于 AES 加密的安全性至关重要。

猜你喜欢

转载自blog.csdn.net/weixin_45047825/article/details/134427170