区块链连接调用完成数据上链

区块链部署相关请参考区块链使用连接工具demo测试MetaMask、Remix和Ganache-CSDN博客

安装区块链相关Geth(基于CentOS7)_geth安装-CSDN博客

1、部署合约生成合约地址和ABI文件

服务器上文件目录结构:/home/tower/back-end/deploy/deploy_contract.py

python 复制代码
import os
from dotenv import load_dotenv
from web3 import Web3
from solcx import compile_source, install_solc

# 加载环境变量(创建.env文件配置以下参数)
load_dotenv()

# -------------------------- 配置参数(需修改为你的私有链信息)--------------------------
GETH_NODE_URL = os.getenv("GETH_NODE_URL", "http://8.152.4.160:8545")  # Geth RPC地址
PRIVATE_KEY = os.getenv("PRIVATE_KEY")  # MetaMask账户私钥(需有足够ETH)
SOLC_VERSION = "0.8.17"  # 合约编译器版本
# -------------------------------------------------------------------------------------

# 1. 安装指定版本Solidity编译器
install_solc(SOLC_VERSION)

# 2. 智能合约源码(与之前设计一致)
CONTRACT_SOURCE_CODE = """
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract BatteryDataChain {
    struct BatteryHealthData {
        string batteryCode;
        uint256 dataUpdateTime;
        uint256 remainingPower;
        uint256 cycleTimes;
        uint256 totalMileage;
        uint256 currentVoltage;
        uint256 onChainTime;
    }

    mapping(string => mapping(uint256 => BatteryHealthData)) private batteryDataMap;
    mapping(string => uint256[]) private batteryTimeStamps;

    event DataStored(string indexed batteryCode, uint256 indexed dataUpdateTime, uint256 onChainTime);

    function storeBatteryData(
        string calldata _batteryCode,
        uint256 _dataUpdateTime,
        uint256 _remainingPower,
        uint256 _cycleTimes,
        uint256 _totalMileage,
        uint256 _currentVoltage
    ) external {
        BatteryHealthData memory data = BatteryHealthData({
            batteryCode: _batteryCode,
            dataUpdateTime: _dataUpdateTime,
            remainingPower: _remainingPower,
            cycleTimes: _cycleTimes,
            totalMileage: _totalMileage,
            currentVoltage: _currentVoltage,
            onChainTime: block.timestamp
        });

        batteryDataMap[_batteryCode][_dataUpdateTime] = data;
        batteryTimeStamps[_batteryCode].push(_dataUpdateTime);
        emit DataStored(_batteryCode, _dataUpdateTime, block.timestamp);
    }

    function queryByDeviceIdAndTime(string calldata _batteryCode, uint256 _dataUpdateTime) external view returns (BatteryHealthData memory) {
        return batteryDataMap[_batteryCode][_dataUpdateTime];
    }

    function queryByDeviceId(string calldata _batteryCode) external view returns (uint256[] memory, BatteryHealthData[] memory) {
        uint256[] memory times = batteryTimeStamps[_batteryCode];
        BatteryHealthData[] memory datas = new BatteryHealthData[](times.length);
        for (uint256 i = 0; i < times.length; i++) {
            datas[i] = batteryDataMap[_batteryCode][times[i]];
        }
        return (times, datas);
    }
}
"""

def deploy_contract():
    # 3. 连接Geth私有链
    w3 = Web3(Web3.HTTPProvider(GETH_NODE_URL))
    if not w3.is_connected():
        raise Exception("无法连接到Geth私有链,请检查节点地址!")
    print("✅ 成功连接到Geth私有链")

    # 4. 编译合约
    compiled_sol = compile_source(
        CONTRACT_SOURCE_CODE,
        solc_version=SOLC_VERSION,
        output_values=["abi", "bin"]
    )
    contract_id, contract_interface = compiled_sol.popitem()
    abi = contract_interface["abi"]
    bytecode = contract_interface["bin"]
    print("✅ 合约编译完成")

    # 5. 构建合约对象
    BatteryContract = w3.eth.contract(abi=abi, bytecode=bytecode)

    # 6. 准备部署交易
    account = w3.eth.account.from_key(PRIVATE_KEY)
    nonce = w3.eth.get_transaction_count(account.address)  # 获取账户nonce
    deploy_tx = BatteryContract.constructor().build_transaction({
        "from": account.address,
        "nonce": nonce,
        "gasPrice": w3.eth.gas_price,
        "gas": 3000000  # 私有链可适当调高gas限制
    })

    # 7. 签名并发送交易
    signed_tx = account.sign_transaction(deploy_tx)
    tx_hash = w3.eth.send_raw_transaction(signed_tx.raw_transaction)
    print(f"📤 部署交易已发送,交易哈希:{w3.to_hex(tx_hash)}")

    # 8. 等待交易确认
    tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
    contract_address = tx_receipt["contractAddress"]
    print(f"🎉 合约部署成功!合约地址:{contract_address}")

    # 9. 保存ABI和合约地址到文件(供后端使用)
    with open("../backend/contract_abi.json", "w") as f:
        import json
        json.dump(abi, f)
    with open("../backend/contract_address.txt", "w") as f:
        f.write(contract_address)
    print("✅ ABI和合约地址已保存到本地文件")

if __name__ == "__main__":
    try:
        deploy_contract()
    except Exception as e:
        print(f"❌ 部署失败:{str(e)}")

部署完成后,/home/tower/back-end/backend/ 目录下会多出两个文件:

  • contract_abi.json --- 合约的接口定义(告诉后端有哪些函数可以调用)
  • contract_address.txt --- 合约在链上的地址(0xCCe205aEDC0BB878632A1751402df976cE1bF52A
步骤 代码 做了什么
连接节点 Web3(Web3.HTTPProvider(GETH_NODE_URL)) 通过 HTTP 连接到 8.152.4.160:8545 的 Geth 节点
编译合约 compile_source(..., solc_version="0.8.17") 用 Solidity 编译器把源码编译成 ABI + 字节码
派生账户 w3.eth.account.from_key(PRIVATE_KEY) 从私钥推出你的账户地址
构建交易 BatteryContract.constructor().build_transaction(...) 构造部署合约的交易
签名+发送 account.sign_transaction(deploy_tx)send_raw_transaction 用私钥签名后广播到链上
等待确认 wait_for_transaction_receipt 等矿工打包,拿到合约地址
保存产物 contract_abi.json + contract_address.txt 这两个文件是部署阶段的输出,也是后端连接区块链的桥梁

2、区块链连接合约

/home/tower/back-end/backend/chain_interaction.py 文件的 get_contract() 函数建立连接:

python 复制代码
def get_contract():
    w3 = Web3(Web3.HTTPProvider(GETH_NODE_URL))          # 连到同一个 Geth 节点
    contract = w3.eth.contract(address=CONTRACT_ADDRESS, abi=CONTRACT_ABI)  # 拿到合约对象
    return w3, contract

有了 contract 对象之后,系统就能调用合约函数了。分两种操作:

写操作 --- 需要私钥签名:

python 复制代码
account = w3.eth.account.from_key(PRIVATE_KEY)           # 用私钥派生账户
tx = contract.functions.storeBatteryData(...).build_transaction({...})
signed_tx = account.sign_transaction(tx)                 # 签名
w3.eth.send_raw_transaction(signed_tx.raw_transaction)   # 发送

读操作--- 此时不需要私钥:

python 复制代码
data = contract.functions.queryByDeviceId(battery_code).call()  # 直接读,免费且无需签名

3、区块链连接账户

参考安装区块链相关Geth(基于CentOS7)_geth安装-CSDN博客

Step 1 安装好区块链后,geth init genesis.json ------ 只是"初始化账本"

动作 结果
创建数据目录结构 生成 data/geth/chaindata 等数据库文件
写入创世块 账本里有了区块 0
记录 alloc 0x486d...0648 的 1000 ETH 写进世界状态

注意:它不生成私钥、不写 keystore、不产生账号。它只是"账本上记了一笔:这个地址有 1000 ETH"。这笔钱是谁的、有没有钥匙,它不管。

Step 2 启动节点 geth --networkid 1337 --mine ------ 只是"让链跑起来"

它做的事:进程运行、监听端口、开始出块、给矿工地址发奖励。

它同样不创建账号。它只是开始"运营"上面那个账本。

Step 3. 账号到底在哪一步创建?

只有 geth account new (或 console 里的 personal.newAccount())会创建账号,它的产物是:

复制代码
私钥(随机256位)→ 推导出地址 → 私钥用密码加密后写成 keystore 文件

上述的 keystore 文件 UTC--2025-11-23...9054...a2beb 就是这么来的------它是独立于创世块的另一次操作的产物。

将上述 keyStore 中 UTC-文件进行解码,即可得到private_key。

解码文件:

python 复制代码
from eth_account import Account
import json
 
# 替换为你的 keystore 文件路径
with open("UTC--2025-06-17T03-05-18.222102348Z--5c993678d3358462619906c9d5fbc93e98f1490a") as keyfile:
    encrypted_key = keyfile.read()
    password = "123"  # 你设置的密码
    private_key = Account.decrypt(encrypted_key, password)
    print("Private key (hex):", private_key.hex())

4、检查区块链账户

Step 1 --- 检查现状

复制代码
geth attach http://8.152.4.160:8545 -exec 'eth.accounts'              # 有哪些账户
geth attach http://8.152.4.160:8545 -exec 'eth.coinbase'              # 当前矿工地址
geth attach http://8.152.4.160:8545 -exec 'eth.mining'                # 是否在挖(true/false)
geth attach http://8.152.4.160:8545 -exec 'eth.blockNumber'           # 当前区块高度

Step 2 --- 把矿工地址设为 eth.accounts0

复制代码
geth attach http://8.152.4.160:8545 -exec 'miner.setEtherbase(eth.accounts[0])'
# 返回 true 即成功;再执行 eth.coinbase 确认已变成该地址

Step 3 --- 解锁账户(如果是 Clique/PoA 私有链,这步是必须的;纯 PoW 可以跳过):

复制代码
geth attach http://8.152.4.160:8545 -exec 'personal.unlockAccount(eth.accounts[0], "你的密码", 0)'

0 表示永久解锁(直到节点重启)。注意:这个密码就是你创建 keystore 时设的密码(就是解私钥用的同一个密码)。

Step 4 --- 启动挖矿

复制代码
geth attach http://8.152.4.160:8545 -exec 'miner.start()'

Step 5 --- 验证在挖 + 奖励到账(隔一会儿再看):

复制代码
geth attach http://8.152.4.160:8545 -exec 'eth.mining'                          # true
geth attach http://8.152.4.160:8545 -exec 'eth.blockNumber'                     # 数字在涨
geth attach http://8.152.4.160:8545 -exec 'eth.getBalance(eth.accounts[0])'     # 余额在涨
geth attach http://8.152.4.160:8545 -exec 'miner.getHashrate()'                 # 算力

setp 6 --- 停止挖矿

复制代码
geth attach http://8.152.4.160:8545 -exec 'miner.stop()'

5、修改环境变量 .env文件

检查完当前挖矿账户后,在步骤3中解码 private_key,将获取到的解码 key 放在/home/tower/back-end/backend/下的 .env文件中,至此合约文件、数据上区块链与私有链绑定成功,通过 PRIVATE_KEY = os.getenv("PRIVATE_KEY") 获取,进行交易即可完成。

相关推荐
网渡科技19 小时前
RWA解决供应链金融中的痛点:从应收账款数字化到链上资产融资
金融·区块链
大力财经1 天前
抖音生活服务品牌零售行业峰会在杭州举办,探索线下生意新增量
大数据·人工智能·区块链
链上日记2 天前
从公开信息看WEEX的运营轨迹
大数据·区块链
baopixiaoz2 天前
BeeQuant × BeeAgent:AI量化新范式
大数据·人工智能·python·区块链
磐链科技2 天前
去中心化交易所(DEX)开发实战:从Uniswap V2合约设计到生产环境上线
去中心化·区块链
Web3_Daisy2 天前
Robinhood Chain 的 Meme 热潮背后:RWA 正在成为新的流动性入口
区块链
cmes_love2 天前
国内期货Level2五档行情与逐笔成交数据介绍
数据库·区块链
DevOpenClub2 天前
多市场行情如何统一时间与币种:A 股、港股、美股监控数据契约
区块链
cmes_love2 天前
CME和CBOT外盘期货数据下载资源介绍
数据库·区块链