在开始构建区块链档案系统之前,必须先配置好本地开发环境。我们将使用 Python 作为后端语言,Ganache 作为本地以太坊节点,Web3 集成库进行交互。
打开终端,首先确保安装了 Python 3.8 或更高版本。随后执行以下命令安装核心依赖库:
pip install web3 eth-account
如果遇到安装速度慢的问题,请使用国内镜像源:
pip install web3 eth-account -i https://pypi.tuna.tsinghua.edu.cn/simple
我们需要一个本地的区块链网络来模拟存证过程。下载并安装 Ganache(推荐使用 GUI 版本以便直观查看区块状态),或者直接使用命令行版本:
npm install -g ganache
ganache -h 0.0.0.0 -p 7545
执行后,本地会在 7545 端口启动一个模拟以太坊网络,默认会生成 10 个测试账户,每个账户预存 100 ETH。请记录下第一个账户的地址和私钥,后续脚本中需要用到。
智能合约是档案存证的核心,负责在链上记录档案的唯一指纹(哈希值)和元数据。
打开在线 IDE Remix Ethereum IDE (https://remix.ethereum.org/),新建一个文件名为 ArchiveRegistry.sol,并粘贴以下完整代码:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ArchiveRegistry {
// 定义档案结构体
struct Archive {
string metadata; // 档案描述信息
address uploader; // 上传者地址
uint256 timestamp; // 上传时间戳
bool exists; // 是否存在
}
// 映射:文件哈希 -> 档案详情
mapping(string => Archive) private archives;
// 事件:记录存证行为,便于前端监听
event ArchiveStored(string indexed fileHash, string metadata, address uploader, uint256 timestamp);
// 存证函数
function storeArchive(string memory _fileHash, string memory _metadata) public {
require(!archives[_fileHash].exists, "Archive already exists.");
archives[_fileHash] = Archive({
metadata: _metadata,
uploader: msg.sender,
timestamp: block.timestamp,
exists: true
});
emit ArchiveStored(_fileHash, _metadata, msg.sender, block.timestamp);
}
// 验证函数:查询档案是否存在并返回详情
function verifyArchive(string memory _fileHash) public view returns (string memory, address, uint256) {
require(archives[_fileHash].exists, "Archive not found.");
return (
archives[_fileHash].metadata,
archives[_fileHash].uploader,
archives[_fileHash].timestamp
);
}
}
在 Remix 中,切换到 Solidity Compiler 标签页,点击 Compile ArchiveRegistry.sol。编译成功后,切换到 Deployment 标签页,在 At Address 或下方区域找到 ABI 和 Bytecode(通常在编译详情按钮中)。
将生成的 ABI(JSON 格式)和 Bytecode(0x 开头的字符串)复制下来,分别保存为本地文件 abi.json 和 bytecode.txt,或者直接粘贴到下文的 Python 脚本中对应的变量位置。

本部分将编写 Python 脚本,自动扫描本地文件夹,计算文件哈希,并将哈希值上链存证。
脚本主要包含三个步骤:读取本地文件并计算 SHA-256 哈希值,确保文件内容不可篡改;连接本地 Ganache 节点;构建交易并调用智能合约的 storeArchive 方法。
新建文件 upload_archives.py,将以下代码完整复制进去。注意替换 YOUR_PRIVATE_KEY 和 CONTRACT_ADDRESS。
import json
import os
import hashlib
import time
from web3 import Web3
from eth_account import Account
================= 配置区域 =================
Ganache 本地节点地址
GANACHE_URL = "http://127.0.0.1:7545"
替换为 Ganache 中显示的私钥(不带 0x 前缀或带均可)
PRIVATE_KEY = "YOUR_PRIVATE_KEY"
替换为部署合约后生成的合约地址
CONTRACT_ADDRESS = "YOUR_CONTRACT_ADDRESS"
需要整理存证的本地文件夹路径
FOLDER_PATH = "./archives_data"
===========================================
def connect_to_blockchain():
"""连接区块链节点"""
w3 = Web3(Web3.HTTPProvider(GANACHE_URL))
if not w3.is_connected():
raise Exception("无法连接到区块链节点,请检查 Ganache 是否启动。")
return w3
def get_file_hash(filepath):
"""计算文件的 SHA-256 哈希值"""
sha256_hash = hashlib.sha256()
with open(filepath, "rb") as f:
分块读取大文件
for byte_block in iter(lambda: f.read(4096), b""):
sha256_hash.update(byte_block)
return sha256_hash.hexdigest()
def deploy_contract_interaction(w3, abi, contract_address, file_hash, metadata):
"""与智能合约交互"""
account = Account.from_key(PRIVATE_KEY)
address = account.address
contract = w3.eth.contract(address=contract_address, abi=abi)
构建交易
nonce = w3.eth.get_transaction_count(address)
tx = contract.functions.storeArchive(file_hash, metadata).build_transaction({
'chainId': 1337, Ganache 默认 Chain ID
'gas': 2000000,
'gasPrice': w3.eth.gas_price,
'nonce': nonce,
})
签名交易
signed_tx = w3.eth.account.sign_transaction(tx, PRIVATE_KEY)
发送交易
tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
等待交易回执
print(f" 交易发送中... Hash: {tx_hash.hex()}")
receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
print(f" 交易确认成功! Block: {receipt.blockNumber}")
return receipt
def main():
1. 加载 ABI (请确保 abi.json 文件在同目录下,或直接填入 JSON 字符串)
with open('abi.json', 'r', encoding='utf-8') as f:
abi = json.load(f)
2. 连接节点
w3 = connect_to_blockchain()
print(f"成功连接节点,当前区块高度: {w3.eth.block_number}")
3. 遍历文件夹
if not os.path.exists(FOLDER_PATH):
os.makedirs(FOLDER_PATH)
print(f"文件夹 {FOLDER_PATH} 不存在,已创建。请放入文件后重试。")
return
files = os.listdir(FOLDER_PATH)
print(f"扫描到 {len(files)} 个文件,开始处理...")
for filename in files:
filepath = os.path.join(FOLDER_PATH, filename)
if os.path.isfile(filepath):
print(f"正在处理文件: {filename}")
计算哈希
file_hash = get_file_hash(filepath)
print(f" 文件哈希: {file_hash}")
构造元数据 (示例:文件名+当前时间)
metadata = f"Archive: {filename}, uploaded at {int(time.time())}"
上链
try:
deploy_contract_interaction(w3, abi, CONTRACT_ADDRESS, file_hash, metadata)
print(f" >>> {filename} 存证完成 <<<")
except Exception as e:
print(f" 错误: {e}")
print("-" 40)
if __name__ == "__main__":
main()
代码准备就绪后,进行实际操作测试。
在项目根目录下创建一个名为 archives_data 的文件夹,放入几个测试文件(如 contract.pdf, image.jpg)。
确保 Ganache 正在运行,且 abi.json、bytecode.txt(如需部署)和脚本在同一目录。配置好脚本中的 PRIVATE_KEY 和 CONTRACT_ADDRESS。运行命令:
python upload_archives.py
终端将输出文件哈希计算过程和交易回执信息。如果看到“交易确认成功”,说明数据已写入区块链。
打开 Ganache GUI 界面,点击 TRANSACTIONS 标签页,可以看到最新的交易记录。点击具体的交易,可以查看 Input Data,其中包含了我们调用的方法名及参数(文件哈希)。
可以在 Remix 的 Deployed Contracts 区域,展开合约,调用 verifyArchive 函数,输入终端打印出的文件哈希,点击调用。如果返回了对应的元数据和上传时间,则证明档案存证逻辑完全正确且数据上链无误。