《区块链公链数据分析简易速速上手小册》第3章:区块链数据结构(2024 最新版)(上)+https://developer.aliyun.com/article/1486952
智能合约示例(Solidity)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; contract SimpleStorage { uint public storedData; event ValueChanged(uint oldValue, uint newValue); function set(uint x) public { emit ValueChanged(storedData, x); storedData = x; } }
步骤1:安装 Web3.py
确保你的环境中已安装Web3.py:
pip install web3
步骤2:编写 Python 脚本以监听事件
接下来,我们将编写一个Python脚本,该脚本通过Infura连接到以太坊网络,并监听ValueChanged事件。
from web3 import Web3 # 使用Infura的WebSocket服务连接到以太坊网络 w3 = Web3(Web3.WebsocketProvider('wss://mainnet.infura.io/ws/v3/YOUR_INFURA_PROJECT_ID')) # 智能合约地址和ABI contract_address = '0xYourContractAddress' contract_abi = json.loads('[YOUR_CONTRACT_ABI]') contract = w3.eth.contract(address=contract_address, abi=contract_abi) # 创建事件过滤器 value_changed_filter = contract.events.ValueChanged.createFilter(fromBlock='latest') print("Listening for ValueChanged events...") # 轮询以检查新事件 while True: for event in value_changed_filter.get_new_entries(): print(f"Old Value: {event['args']['oldValue']}, New Value: {event['args']['newValue']}") time.sleep(10)
在这段代码中,将YOUR_INFURA_PROJECT_ID替换为你的Infura项目ID,0xYourContractAddress替换为智能合约的地址,[YOUR_CONTRACT_ABI]替换为合约的ABI。这段脚本首先创建了一个事件过滤器,然后进入一个循环,不断检查并打印新的ValueChanged事件。
注意事项
- 使用WebSocket提供者(
WebsocketProvider)对于监听事件是必需的,因为它支持订阅和实时更新,而HTTP提供者不支持这些功能。 - 在生产环境中,你可能需要考虑错误处理和重新连接逻辑,以确保应用能够持续稳定地监听事件。
通过这个案例,我们展示了如何监听并处理以太坊智能合约中的事件,这对于构建响应式和交互式的DApps至关重要。监听智能合约事件是区块链开发中的一个强大工具,它可以帮助开发者实时捕捉到链上发生的关键活动,并据此执行相应的逻辑。
3.2.4 拓展案例 2:向智能合约发送交易
在以太坊上,向智能合约发送交易是一种常见的操作,用于执行合约的函数。这种操作可以改变合约的状态或触发链上的某些活动。以下案例演示了如何使用Python和Web3.py向以太坊智能合约发送交易。
假设我们有一个简单的智能合约,它允许存储和更新一个数值。
智能合约示例(Solidity)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; contract SimpleStorage { uint public storedData; function set(uint x) public { storedData = x; } }
此合约包含一个名为set的函数,允许更新storedData变量的值。
步骤1:准备环境
确保你已安装Web3.py库:
pip install web3
步骤2:编写Python脚本发送交易
接下来,我们将编写一个Python脚本,通过Infura连接到以太坊网络,并发送一个交易来调用set函数。
from web3 import Web3 from web3.middleware import geth_poa_middleware # 使用Infura的HTTP服务连接到以太坊网络 w3 = Web3(Web3.HTTPProvider('https://rinkeby.infura.io/v3/YOUR_INFURA_PROJECT_ID')) w3.middleware_onion.inject(geth_poa_middleware, layer=0) # 智能合约地址和ABI contract_address = '0xYourContractAddress' contract_abi = json.loads('[YOUR_CONTRACT_ABI]') contract = w3.eth.contract(address=contract_address, abi=contract_abi) # 发送交易需要的账户和私钥 account = '0xYourAccountAddress' private_key = 'YourPrivateKey' # 构建交易 nonce = w3.eth.getTransactionCount(account) tx = contract.functions.set(123).buildTransaction({ 'chainId': 4, # Rinkeby测试网络的chain ID 'gas': 2000000, 'gasPrice': w3.toWei('50', 'gwei'), 'nonce': nonce, }) # 签名交易 signed_tx = w3.eth.account.sign_transaction(tx, private_key) # 发送交易 tx_hash = w3.eth.sendRawTransaction(signed_tx.rawTransaction) # 获取交易哈希值 print(f"Transaction hash: {tx_hash.hex()}")
在这个脚本中,替换YOUR_INFURA_PROJECT_ID、0xYourContractAddress、[YOUR_CONTRACT_ABI]、0xYourAccountAddress和YourPrivateKey为你自己的值。
注意事项
- 安全性:处理私钥时要非常小心,确保不要在公共代码库或不安全的地方暴露私钥。
- Gas费用:发送交易需要支付Gas费用,因此确保你的账户有足够的以太币来支付。
- 测试网络:在实际将交易发送到主网之前,强烈推荐在测试网络(如Rinkeby)上进行测试。
通过这个案例,我们演示了如何向以太坊智能合约发送交易以执行其函数,这是开发去中心化应用(DApps)的一个重要环节。理解如何与智能合约交互、发送交易和管理Gas费用对于区块链开发者来说是基本技能。
通过这些案例,我们展示了如何与以太坊智能合约进行交互,包括读取合约状态、监听事件以及发送交易以调用合约函数。这些技能是开发去中心化应用和进行区块链数据分析的基础,为开发者提供了强大的工具来构建复杂和高效的区块链应用。
3.3 链上与链下数据
在区块链应用开发中,理解链上和链下数据的区别及其应用是至关重要的。这不仅关乎数据的存储和处理方式,也影响到应用的设计和性能。
3.3.1 基础知识
- 链上数据:直接存储在区块链上的数据。它的特点是不可篡改、透明且永久保存,适合存储交易记录、智能合约状态等关键信息。但由于存储成本较高,通常不适合存储大量数据。
- 链下数据:存储在区块链外部的数据。链下数据可以存储在传统数据库、文件系统或任何其他形式的数据存储系统中。它适合存储大量数据,如应用日志、复杂的文档等,但不享有区块链数据的安全性和不可篡改性。
3.3.2 重点案例:链上验证链下数据
在区块链应用开发中,经常面临一个问题:如何有效地存储和验证大量数据。由于直接在链上存储数据成本较高,一种常见的解决方案是将数据存储在链下(如在服务器或分布式文件系统中),同时在链上存储数据的指纹(通常是数据的哈希值)以保证数据的不可篡改性和可验证性。
以下案例演示了如何使用Python和Web3.py以及以太坊智能合约来实现链上验证链下数据的完整性。
步骤1: 智能合约编写
首先,编写一个简单的智能合约,该合约允许用户存储和检索数据哈希值。
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract DataHashStorage { mapping(bytes32 => bool) public hashExists; event HashStored(bytes32 dataHash); function storeDataHash(bytes32 dataHash) public { require(!hashExists[dataHash], "Data hash already stored."); hashExists[dataHash] = true; emit HashStored(dataHash); } function verifyDataHash(bytes32 dataHash) public view returns (bool) { return hashExists[dataHash]; } }
该合约包含一个storeDataHash函数用于存储数据哈希值,和一个verifyDataHash函数用于验证给定的哈希值是否已经被存储。
步骤2: 部署智能合约
使用Remix或Truffle等工具部署智能合约到以太坊网络,并记录合约地址。
步骤3: 使用Python存储和验证数据哈希
首先,安装Web3.py库。
pip install web3
接下来,编写Python脚本来计算链下数据的哈希值,存储这个哈希值到智能合约,并验证它。
from web3 import Web3 import hashlib # 连接到以太坊节点 w3 = Web3(Web3.HTTPProvider('https://rinkeby.infura.io/v3/YOUR_INFURA_PROJECT_ID')) # 智能合约地址和ABI contract_address = '0xYourContractAddress' contract_abi = json.loads('[YOUR_CONTRACT_ABI]') contract = w3.eth.contract(address=contract_address, abi=contract_abi) # 链下数据 data = "This is an example of off-chain data." data_hash = hashlib.sha256(data.encode()).hexdigest() # 发送交易存储数据哈希到智能合约 # 注意: 这里需要账户和私钥进行交易签名 account = w3.eth.account.privateKeyToAccount('YourPrivateKey') nonce = w3.eth.getTransactionCount(account.address) tx = contract.functions.storeDataHash(data_hash).buildTransaction({ 'chainId': 4, # Rinkeby测试网络的chain ID 'gas': 200000, 'gasPrice': w3.toWei('50', 'gwei'), 'nonce': nonce, }) signed_tx = w3.eth.account.sign_transaction(tx, 'YourPrivateKey') tx_hash = w3.eth.sendRawTransaction(signed_tx.rawTransaction) # 等待交易被挖矿 tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash) # 验证数据哈希 verified = contract.functions.verifyDataHash(data_hash).call() print(f"Data hash verified: {verified}")
在这个脚本中,替换YOUR_INFURA_PROJECT_ID、0xYourContractAddress、[YOUR_CONTRACT_ABI]和YourPrivateKey为你自己的值。
结论
通过这个案例,我们展示了如何将链下数据的哈希值存储到以太坊智能合约中,并实现了一个机制来验证链下数据的完整性和真实性。这种方法结合了区块链的不可篡改性和链下数据存储的灵活性与成本效益,为开发复杂的区块链应用提供了一种有效的数据管理策略。
3.3.3 拓展案例 1:使用 IPFS 存储链下数据
IPFS(InterPlanetary File System)是一个分布式文件系统,它允许存储和访问大量的数据,同时提供了一个唯一的哈希值用于数据的访问和验证。IPFS是链下数据存储的理想选择,因为它提供了去中心化和持久性,同时能够通过链上存储的哈希值来验证数据的完整性。以下案例演示了如何使用Python将数据存储到IPFS,并将对应的哈希值记录在以太坊智能合约中。
步骤1: 安装IPFS和Python客户端
首先,确保你的系统中安装了IPFS。你可以从IPFS官网下载并安装IPFS。
然后,安装Python的IPFS客户端库:
pip install ipfshttpclient
步骤2: 启动IPFS守护进程
在终端运行以下命令来启动IPFS守护进程:
ipfs daemon
步骤3: 使用Python脚本存储数据到IPFS
接下来,我们将使用Python脚本来存储数据到IPFS,并获取数据的唯一哈希值。
import ipfshttpclient # 连接到本地IPFS节点 client = ipfshttpclient.connect('/ip4/127.0.0.1/tcp/5001/http') # 要存储到IPFS的数据 data = "Hello, IPFS and Ethereum!" # 添加数据到IPFS res = client.add_str(data) ipfs_hash = res print(f"Data stored on IPFS with hash: {ipfs_hash}")
步骤4: 将IPFS哈希值存储到以太坊智能合约
现在,我们有了存储在IPFS上的数据哈希值,下一步是将这个哈希值存储到以太坊智能合约中,以便链上验证。
假设我们已经有一个智能合约,它有一个函数可以存储和验证IPFS哈希值。我们将使用Web3.py来与以太坊交互。
from web3 import Web3 # 连接到以太坊 w3 = Web3(Web3.HTTPProvider('https://rinkeby.infura.io/v3/YOUR_INFURA_PROJECT_ID')) # 智能合约地址和ABI contract_address = '0xYourContractAddress' contract_abi = '[YOUR_CONTRACT_ABI]' contract = w3.eth.contract(address=contract_address, abi=contract_abi) # 账户和私钥 account = '0xYourAccountAddress' private_key = 'YourPrivateKey' # 构建交易 nonce = w3.eth.getTransactionCount(account) tx = contract.functions.storeIPFSHash(ipfs_hash).buildTransaction({ 'chainId': 4, # Rinkeby测试网络的chain ID 'gas': 200000, 'gasPrice': w3.toWei('50', 'gwei'), 'nonce': nonce, }) # 签名交易 signed_tx = w3.eth.account.sign_transaction(tx, private_key) # 发送交易 tx_hash = w3.eth.sendRawTransaction(signed_tx.rawTransaction) # 获取交易哈希值 print(f"Transaction hash: {tx_hash.hex()}")
在这个脚本中,替换YOUR_INFURA_PROJECT_ID、0xYourContractAddress、[YOUR_CONTRACT_ABI]、0xYourAccountAddress和YourPrivateKey为你自己的值。
结论
通过这个案例,我们演示了如何结合使用IPFS和以太坊来存储和验证大量的链下数据。这种方法利用了IPFS的分布式存储能力和以太坊智能合约的不可篡改性,为构建去中心化应用提供了一个高效、安全的数据存储和验证解决方案。
3.3.4 拓展案例 2:链下数据的链上验证
链下数据的链上验证是确保数据完整性和真实性的关键机制,特别是在去中心化应用(DApp)中处理大量数据时。这个过程通常涉及两个步骤:首先将数据存储在链下,然后将数据的哈希值或数字指纹存储在链上。当需要验证数据时,可以重新计算其哈希值并与链上存储的哈希值进行比对。
在本案例中,我们将使用Python演示如何实现链下数据的链上验证,包括如何通过智能合约存储和验证数据哈希。
步骤1: 智能合约编写
假设我们有一个简单的智能合约,用于存储和验证数据哈希:
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; contract DataVerifier { mapping(bytes32 => bool) private hashes; // 事件,用于记录数据哈希已存储 event HashStored(bytes32 dataHash); // 存储数据哈希 function storeHash(bytes32 dataHash) public { require(!hashes[dataHash], "Hash already stored."); hashes[dataHash] = true; emit HashStored(dataHash); } // 验证数据哈希 function verifyHash(bytes32 dataHash) public view returns (bool) { return hashes[dataHash]; } }
步骤2: 部署智能合约
使用你偏好的工具(如Truffle、Hardhat或Remix)将智能合约部署到以太坊网络(主网或测试网)。
步骤3: 使用Python存储和验证数据哈希
首先,确保安装了Web3.py库:
pip install web3
接下来,我们将计算链下数据的哈希值,通过智能合约存储这个哈希值,然后验证它。
from web3 import Web3 import hashlib # 连接到以太坊 w3 = Web3(Web3.HTTPProvider('https://rinkeby.infura.io/v3/YOUR_INFURA_PROJECT_ID')) # 智能合约地址和ABI contract_address = '0xYourContractAddress' contract_abi = '[YOUR_CONTRACT_ABI]' contract = w3.eth.contract(address=contract_address, abi=contract_abi) # 链下数据 data = "This is an example of off-chain data." data_hash = hashlib.sha256(data.encode()).hexdigest() # 转换哈希值为bytes32 data_hash_bytes32 = w3.toBytes(hexstr=data_hash) # 发送交易存储数据哈希到智能合约 account = '0xYourAccountAddress' private_key = 'YourPrivateKey' nonce = w3.eth.getTransactionCount(account) tx = contract.functions.storeHash(data_hash_bytes32).buildTransaction({ 'chainId': 4, # Rinkeby测试网络的chain ID 'gas': 2000000, 'gasPrice': w3.toWei('50', 'gwei'), 'nonce': nonce, }) signed_tx = w3.eth.account.sign_transaction(tx, private_key) tx_hash = w3.eth.sendRawTransaction(signed_tx.rawTransaction) print(f"Transaction hash: {tx_hash.hex()}") # 等待交易被挖掘 tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash) # 验证数据哈希 verified = contract.functions.verifyHash(data_hash_bytes32).call() print(f"Data hash verified: {verified}")
在这个脚本中,替换YOUR_INFURA_PROJECT_ID、0xYourContractAddress、[YOUR_CONTRACT_ABI]、0xYourAccountAddress和YourPrivateKey为你自己的值。
结论
这个案例展示了如何利用智能合约在链上存储和验证链下数据的哈希值。这种机制增加了数据处理的透明度和安全性,同时依然能够利用链下存储的高效性和低成本。这对于那些需要处理大量数据的去中心化应用尤其有用,例如在供应链管理、版权认证和数据市场等领域。
通过这些案例,我们探讨了链上与链下数据的不同用途和如何将它们结合使用。存储关键验证信息到链上,同时将大量数据存储到链下,是一种既能利用区块链技术的安全性和透明度,又能有效管理成本和性能的方法。