基于FISCO BCOS 部署 Solidity投票智能合约 并基于GO SDK进行合约调用指南

目录

概要设计

完整流程实现

一、环境准备

[1.1 安装FISCO BCOS节点](#1.1 安装FISCO BCOS节点)

[1.2 安装Go SDK依赖](#1.2 安装Go SDK依赖)

二、Solidity投票智能合约

[2.1 创建投票合约 Voting.sol](#2.1 创建投票合约 Voting.sol)

[2.2 编译合约](#2.2 编译合约)

[三、Go SDK合约调用实现](#三、Go SDK合约调用实现)

[3.1 项目结构](#3.1 项目结构)

[3.2 Go SDK客户端封装](#3.2 Go SDK客户端封装)

[3.3 投票合约调用示例](#3.3 投票合约调用示例)

[3.4 配置文件 conf/config.toml](#3.4 配置文件 conf/config.toml)

四、测试脚本

五、Web3.js前端集成示例(可选)

六、部署和测试步骤总结

注意事项


FISCO BCOS是著名的国产的企业级区块链平台,支持Solidity智能合约,并且提供了多种语言的SDK,包括Go。

FISCO BCOS 3.x版本采用微服务模块化设计架构,总体上系统包含接入层、调度层、计算层、存储层和管理层五个方面。下面分别介绍每层的功能设计。

  • 接入层 :接入层主要负责区块链连接的能力,包括提供P2P能力的"对外网关服务"和提供给SDK访问的"对内网关服务"。在联盟链的体系中,"对外网关服务"管理了机构对外连接的出入口,负责机构级别的安全认证。"对内网关服务"则提供给机构内的客户端(应用端)访问入口。两个网关服务都可以平行扩展、多活部署、负载均衡,满足高可用要求。

  • 调度层 :调度层是区块链内核运转调度的"大脑中枢"系统,负责整个区块链系统运行调度,包括网络分发调度、交易池管理、共识机制、计算调度等模块。其中,网络分发模块主要是与接入层实现互联通信功能,处理消息分发逻辑;交易池管理主要负责交易的接收、签名验证、淘汰等功能;共识机制负责交易排序、区块打包以及对区块结果进行分布式共识,确保一致性;计算调度则完成交易验证(核心是智能合约的验证)的调度处理,实现并行验证,是整个系统吞吐量的关键。

  • 计算层 :这里主要负责交易验证,需要将交易解码放入合约虚拟机中执行,得到交易执行结果。交易验证是整个区块链的核心,尤其是基于智能合约的区块链系统,交易验证的计算可能需要花费较大的CPU开销。因此,如何实现并行化交易验证,通过集群化模式实现交易验证计算的平行扩展是非常重要的。

  • 存储层 :存储层负责落盘存储交易、区块、账本状态等数据,存储层重点关注如何支撑海量数据的存储,采用分布式存储集群的方式可实现存储容量可扩展。分布式存储业界已有许多稳定可复用的开源组件(如TiKV),这层将复用成熟组件。

  • 管理层 :管理层是为整个区块链系统各模块实现可视化管理的平台,包括部署、配置、日志、网络路由等管理功能。FISCO BCOS 3.x系统架构基于开源微服务框架Tars构建,这层的能力复用成熟的Tars-Framework管理组件。

  • 上面介绍了FISCO BCOS区块链平台,下面我将介绍如何基于FISCO BCOS部署一个投票智能合约,并使用Go SDK进行合约调用。

概要设计

一、步骤概览:

  1. 1、环境准备:安装必要的工具和依赖,包括FISCO BCOS节点、Go环境、Go SDK等。

  2. 2、编写Solidity投票智能合约。

  3. 3、编译智能合约,获取ABI和BIN。

  4. 4、部署智能合约到FISCO BCOS链上。

  5. 5、使用Go SDK编写合约调用代码。

二、详细步骤:

完整流程实现

  1. 1、环境准备

  2. 2、编写投票智能合约(Vote.sol)

    以下是一个简单的投票合约示例,包含提案和投票功能。

javascript 复制代码
pragma solidity ^0.4.24;

contract Voting {
    // 投票者结构
    struct Voter {
        uint weight; // 权重,这里简单处理,默认1
        bool voted;  // 是否已投票
        uint vote;   // 所投提案的索引
    }

    // 提案结构
    struct Proposal {
        bytes32 name;   // 提案名称
        uint voteCount; // 得票数
    }

    address public chairperson; // 主持人
    mapping(address => Voter) public voters; // 投票人地址到投票人的映射
    Proposal[] public proposals; // 提案数组

    // 构造函数,初始化提案列表
    constructor(bytes32[] proposalNames) public {
        chairperson = msg.sender;
        voters[chairperson].weight = 1;

        for (uint i = 0; i < proposalNames.length; i++) {
            proposals.push(Proposal({
                name: proposalNames[i],
                voteCount: 0
            }));
        }
    }

    // 给投票人授权,只有主持人可以调用
    function giveRightToVote(address voter) public {
        require(msg.sender == chairperson, "Only chairperson can give right to vote.");
        require(!voters[voter].voted, "The voter already voted.");
        require(voters[voter].weight == 0);
        voters[voter].weight = 1;
    }

    // 投票函数
    function vote(uint proposal) public {
        Voter storage sender = voters[msg.sender];
        require(sender.weight != 0, "Has no right to vote");
        require(!sender.voted, "Already voted.");
        sender.voted = true;
        sender.vote = proposal;

        // 计票
        proposals[proposal].voteCount += sender.weight;
    }

    // 计算获胜提案
    function winningProposal() public view returns (uint winningProposal_) {
        uint winningVoteCount = 0;
        for (uint p = 0; p < proposals.length; p++) {
            if (proposals[p].voteCount > winningVoteCount) {
                winningVoteCount = proposals[p].voteCount;
                winningProposal_ = p;
            }
        }
    }

    // 获取获胜提案名称
    function winnerName() public view returns (bytes32 winnerName_) {
        winnerName_ = proposals[winningProposal()].name;
    }
}
  1. 3、编译智能合约

    • 可以使用solc编译器或者在线编译器(如Remix)编译合约,得到ABI和BIN文件。

    • 也可以使用FISCO BCOS提供的控制台工具来编译。

  2. 4、部署智能合约

    • 使用Go SDK的合约部署功能,需要合约的BIN和ABI。
  3. 5、使用Go SDK进行合约调用

    • 首先,需要初始化客户端,连接到FISCO BCOS节点。

    • 然后,使用ABI和合约地址加载合约实例。

    • 最后,调用合约的方法。

  4. 接下来详细介绍基于FISCO BCOS部署Solidity投票智能合约并使用Go SDK调用的完整流程。

一、环境准备

1.1 安装FISCO BCOS节点
bash 复制代码
# 下载安装脚本
curl -LO https://github.com/FISCO-BCOS/FISCO-BCOS/releases/download/v3.4.0/build_chain.sh && chmod u+x build_chain.sh

# 构建单节点链
./build_chain.sh -l 127.0.0.1:4 -p 30300,20200,8545

# 启动节点
./nodes/127.0.0.1/start_all.sh

# 检查节点状态
./nodes/127.0.0.1/fisco-bcos -v
1.2 安装Go SDK依赖
bash 复制代码
# 安装Go(版本>=1.17)
# 下载Go SDK
git clone https://github.com/FISCO-BCOS/go-sdk.git
cd go-sdk

# 配置证书(从节点复制)
cp -r nodes/127.0.0.1/sdk/* conf/

二、Solidity投票智能合约

2.1 创建投票合约 Voting.sol
javascript 复制代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Voting {
    // 投票者结构
    struct Voter {
        uint weight;      // 投票权重
        bool voted;       // 是否已投票
        uint vote;        // 投票提案索引
        address delegate; // 委托投票地址
    }
    
    // 提案结构
    struct Proposal {
        bytes32 name;     // 提案名称
        uint voteCount;   // 得票数
    }
    
    address public chairperson; // 主持人
    mapping(address => Voter) public voters; // 投票人映射
    Proposal[] public proposals; // 提案列表
    
    // 事件
    event VoterRegistered(address voter);
    event Voted(address voter, uint proposalIndex);
    event Delegated(address from, address to);
    event ProposalCreated(bytes32 name);
    
    // 构造函数:初始化提案
    constructor(bytes32[] memory proposalNames) {
        chairperson = msg.sender;
        
        // 主持人自动获得投票权
        voters[chairperson].weight = 1;
        
        // 创建提案
        for (uint i = 0; i < proposalNames.length; i++) {
            proposals.push(Proposal({
                name: proposalNames[i],
                voteCount: 0
            }));
            emit ProposalCreated(proposalNames[i]);
        }
    }
    
    // 主持人给投票者授权
    function giveRightToVote(address voter) public {
        require(msg.sender == chairperson, "Only chairperson can give voting rights");
        require(!voters[voter].voted, "The voter already voted");
        require(voters[voter].weight == 0, "Voter already has right");
        
        voters[voter].weight = 1;
        emit VoterRegistered(voter);
    }
    
    // 委托投票
    function delegate(address to) public {
        Voter storage sender = voters[msg.sender];
        require(!sender.voted, "You already voted");
        require(to != msg.sender, "Self-delegation is disallowed");
        
        // 查找最终委托地址
        while (voters[to].delegate != address(0)) {
            to = voters[to].delegate;
            require(to != msg.sender, "Found loop in delegation");
        }
        
        sender.voted = true;
        sender.delegate = to;
        Voter storage delegate_ = voters[to];
        
        if (delegate_.voted) {
            // 如果被委托者已投票,则增加提案票数
            proposals[delegate_.vote].voteCount += sender.weight;
        } else {
            // 否则增加被委托者的权重
            delegate_.weight += sender.weight;
        }
        
        emit Delegated(msg.sender, to);
    }
    
    // 投票
    function vote(uint proposal) public {
        Voter storage sender = voters[msg.sender];
        require(sender.weight != 0, "Has no right to vote");
        require(!sender.voted, "Already voted");
        
        sender.voted = true;
        sender.vote = proposal;
        
        // 计票
        proposals[proposal].voteCount += sender.weight;
        
        emit Voted(msg.sender, proposal);
    }
    
    // 计算获胜提案
    function winningProposal() public view returns (uint winningProposal_) {
        uint winningVoteCount = 0;
        for (uint p = 0; p < proposals.length; p++) {
            if (proposals[p].voteCount > winningVoteCount) {
                winningVoteCount = proposals[p].voteCount;
                winningProposal_ = p;
            }
        }
    }
    
    // 获取获胜提案名称
    function winnerName() public view returns (bytes32 winnerName_) {
        winnerName_ = proposals[winningProposal()].name;
    }
    
    // 获取提案数量
    function getProposalsCount() public view returns (uint) {
        return proposals.length;
    }
    
    // 获取提案详情
    function getProposal(uint index) public view returns (bytes32 name, uint voteCount) {
        require(index < proposals.length, "Invalid proposal index");
        Proposal memory proposal = proposals[index];
        return (proposal.name, proposal.voteCount);
    }
    
    // 获取投票者信息
    function getVoter(address addr) public view returns (uint weight, bool voted, uint vote, address delegate) {
        Voter memory voter = voters[addr];
        return (voter.weight, voter.voted, voter.vote, voter.delegate);
    }
}
2.2 编译合约
bash 复制代码
# 安装solc编译器
npm install -g solc

# 编译合约
solcjs --bin --abi Voting.sol -o build/

三、Go SDK合约调用实现

3.1 项目结构
XML 复制代码
voting-demo/
├── go.mod
├── main.go
├── contracts/
│   └── Voting.sol
├── build/
│   ├── Voting.abi
│   └── Voting.bin
└── conf/
    ├── ca.crt
    ├── sdk.crt
    └── sdk.key
3.2 Go SDK客户端封装
Go 复制代码
// client/client.go
package client

import (
	"context"
	"fmt"
	"log"
	"math/big"
	"strings"

	"github.com/FISCO-BCOS/go-sdk/client"
	"github.com/FISCO-BCOS/go-sdk/conf"
	"github.com/FISCO-BCOS/go-sdk/core/types"
	"github.com/ethereum/go-ethereum"
	"github.com/ethereum/go-ethereum/accounts/abi"
	"github.com/ethereum/go-ethereum/common"
)

type FiscoClient struct {
	config     *conf.Config
	client     *client.Client
	privateKey string
}

func NewFiscoClient(configFile, privateKey string) (*FiscoClient, error) {
	configs, err := conf.ParseConfigFile(configFile)
	if err != nil {
		return nil, fmt.Errorf("parse config failed: %v", err)
	}
	
	if len(configs) == 0 {
		return nil, fmt.Errorf("no config found")
	}
	
	config := configs[0]
	
	cli, err := client.Dial(config)
	if err != nil {
		return nil, fmt.Errorf("dial client failed: %v", err)
	}
	
	return &FiscoClient{
		config:     config,
		client:     cli,
		privateKey: privateKey,
	}, nil
}

func (fc *FiscoClient) DeployContract(abiJSON, binHex string, params ...interface{}) (common.Address, *types.Transaction, error) {
	parsedABI, err := abi.JSON(strings.NewReader(abiJSON))
	if err != nil {
		return common.Address{}, nil, err
	}
	
	input, err := parsedABI.Pack("", params...)
	if err != nil {
		return common.Address{}, nil, err
	}
	
	data := common.FromHex(binHex)
	data = append(data, input...)
	
	address, tx, _, err := fc.client.DeployContract(fc.privateKey, abiJSON, data)
	if err != nil {
		return common.Address{}, nil, err
	}
	
	return address, tx, nil
}

func (fc *FiscoClient) CallContract(address common.Address, abiJSON string, method string, params ...interface{}) ([]interface{}, error) {
	parsedABI, err := abi.JSON(strings.NewReader(abiJSON))
	if err != nil {
		return nil, err
	}
	
	input, err := parsedABI.Pack(method, params...)
	if err != nil {
		return nil, err
	}
	
	msg := ethereum.CallMsg{
		To:   &address,
		Data: input,
	}
	
	output, err := fc.client.CallContract(context.Background(), msg, nil)
	if err != nil {
		return nil, err
	}
	
	return parsedABI.Unpack(method, output)
}

func (fc *FiscoClient) SendTransaction(address common.Address, abiJSON string, method string, params ...interface{}) (*types.Transaction, error) {
	parsedABI, err := abi.JSON(strings.NewReader(abiJSON))
	if err != nil {
		return nil, err
	}
	
	input, err := parsedABI.Pack(method, params...)
	if err != nil {
		return nil, err
	}
	
	tx, err := fc.client.SendTransaction(fc.privateKey, address, input)
	if err != nil {
		return nil, err
	}
	
	return tx, nil
}

func (fc *FiscoClient) GetTransactionReceipt(txHash common.Hash) (*types.Receipt, error) {
	return fc.client.GetTransactionReceipt(context.Background(), txHash, false)
}
3.3 投票合约调用示例
Go 复制代码
// main.go
package main

import (
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"log"
	"strings"

	"github.com/FISCO-BCOS/go-sdk/core/types"
	"github.com/ethereum/go-ethereum/common"
	"voting-demo/client"
)

type VotingContract struct {
	address common.Address
	abi     string
	bin     string
	client  *client.FiscoClient
}

func NewVotingContract(client *client.FiscoClient, abiPath, binPath string) (*VotingContract, error) {
	abiData, err := ioutil.ReadFile(abiPath)
	if err != nil {
		return nil, err
	}
	
	binData, err := ioutil.ReadFile(binPath)
	if err != nil {
		return nil, err
	}
	
	// 清理bin文件中的换行符
	binHex := strings.TrimSpace(string(binData))
	
	return &VotingContract{
		abi:    string(abiData),
		bin:    binHex,
		client: client,
	}, nil
}

func (vc *VotingContract) Deploy(proposalNames []string) error {
	// 将字符串转换为bytes32
	var bytes32Names [][32]byte
	for _, name := range proposalNames {
		var bytes32Name [32]byte
		copy(bytes32Name[:], name)
		bytes32Names = append(bytes32Names, bytes32Name)
	}
	
	address, tx, err := vc.client.DeployContract(vc.abi, vc.bin, bytes32Names)
	if err != nil {
		return fmt.Errorf("deploy failed: %v", err)
	}
	
	vc.address = address
	fmt.Printf("Contract deployed at: %s\n", address.Hex())
	fmt.Printf("Transaction hash: %s\n", tx.Hash().Hex())
	
	// 等待交易确认
	receipt, err := vc.waitForTransaction(tx.Hash())
	if err != nil {
		return err
	}
	
	fmt.Printf("Contract deployed successfully. Gas used: %d\n", receipt.GasUsed)
	return nil
}

func (vc *VotingContract) GiveVotingRight(voterAddress string) error {
	address := common.HexToAddress(voterAddress)
	
	tx, err := vc.client.SendTransaction(vc.address, vc.abi, "giveRightToVote", address)
	if err != nil {
		return err
	}
	
	receipt, err := vc.waitForTransaction(tx.Hash())
	if err != nil {
		return err
	}
	
	fmt.Printf("Voting right given to %s. Status: %d\n", voterAddress, receipt.Status)
	return nil
}

func (vc *VotingContract) Vote(proposalIndex uint) error {
	tx, err := vc.client.SendTransaction(vc.address, vc.abi, "vote", big.NewInt(int64(proposalIndex)))
	if err != nil {
		return err
	}
	
	receipt, err := vc.waitForTransaction(tx.Hash())
	if err != nil {
		return err
	}
	
	fmt.Printf("Voted for proposal %d. Status: %d\n", proposalIndex, receipt.Status)
	return nil
}

func (vc *VotingContract) GetWinner() (string, error) {
	results, err := vc.client.CallContract(vc.address, vc.abi, "winnerName")
	if err != nil {
		return "", err
	}
	
	if len(results) > 0 {
		if winnerName, ok := results[0].([32]byte); ok {
			// 移除末尾的空字节
			return strings.TrimRight(string(winnerName[:]), "\x00"), nil
		}
	}
	
	return "", fmt.Errorf("failed to get winner")
}

func (vc *VotingContract) GetProposalCount() (uint, error) {
	results, err := vc.client.CallContract(vc.address, vc.abi, "getProposalsCount")
	if err != nil {
		return 0, err
	}
	
	if len(results) > 0 {
		if count, ok := results[0].(*big.Int); ok {
			return uint(count.Uint64()), nil
		}
	}
	
	return 0, fmt.Errorf("failed to get proposal count")
}

func (vc *VotingContract) GetProposal(index uint) (string, uint, error) {
	results, err := vc.client.CallContract(vc.address, vc.abi, "getProposal", big.NewInt(int64(index)))
	if err != nil {
		return "", 0, err
	}
	
	if len(results) >= 2 {
		name := results[0].([32]byte)
		voteCount := results[1].(*big.Int)
		
		proposalName := strings.TrimRight(string(name[:]), "\x00")
		return proposalName, uint(voteCount.Uint64()), nil
	}
	
	return "", 0, fmt.Errorf("failed to get proposal")
}

func (vc *VotingContract) GetVoterInfo(address string) (uint, bool, uint, string, error) {
	addr := common.HexToAddress(address)
	results, err := vc.client.CallContract(vc.address, vc.abi, "getVoter", addr)
	if err != nil {
		return 0, false, 0, "", err
	}
	
	if len(results) >= 4 {
		weight := results[0].(*big.Int)
		voted := results[1].(bool)
		voteIndex := results[2].(*big.Int)
		delegate := results[3].(common.Address)
		
		return uint(weight.Uint64()), voted, uint(voteIndex.Uint64()), delegate.Hex(), nil
	}
	
	return 0, false, 0, "", fmt.Errorf("failed to get voter info")
}

func (vc *VotingContract) waitForTransaction(txHash common.Hash) (*types.Receipt, error) {
	for {
		receipt, err := vc.client.GetTransactionReceipt(txHash)
		if err != nil {
			return nil, err
		}
		
		if receipt != nil {
			return receipt, nil
		}
		
		// 等待一段时间再重试
		// 在实际应用中,建议使用更复杂的重试逻辑
		// time.Sleep(1 * time.Second)
	}
}

func main() {
	// 初始化客户端
	fiscoClient, err := client.NewFiscoClient("conf/config.toml", "your_private_key_here")
	if err != nil {
		log.Fatal(err)
	}
	
	// 创建投票合约实例
	votingContract, err := NewVotingContract(fiscoClient, "build/Voting.abi", "build/Voting.bin")
	if err != nil {
		log.Fatal(err)
	}
	
	// 部署合约
	fmt.Println("Deploying voting contract...")
	proposals := []string{"Proposal A", "Proposal B", "Proposal C"}
	err = votingContract.Deploy(proposals)
	if err != nil {
		log.Fatal(err)
	}
	
	// 给投票者授权
	fmt.Println("\nGiving voting rights...")
	voterAddress := "0xYourVoterAddressHere"
	err = votingContract.GiveVotingRight(voterAddress)
	if err != nil {
		log.Fatal(err)
	}
	
	// 投票
	fmt.Println("\nVoting...")
	err = votingContract.Vote(0) // 投给第一个提案
	if err != nil {
		log.Fatal(err)
	}
	
	// 查询投票结果
	fmt.Println("\nGetting voting results...")
	winner, err := votingContract.GetWinner()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Winner: %s\n", winner)
	
	// 查询提案数量
	count, err := votingContract.GetProposalCount()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Total proposals: %d\n", count)
	
	// 查询提案详情
	for i := uint(0); i < count; i++ {
		name, votes, err := votingContract.GetProposal(i)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("Proposal %d: %s - %d votes\n", i, name, votes)
	}
	
	// 查询投票者信息
	weight, voted, voteIndex, delegate, err := votingContract.GetVoterInfo(voterAddress)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("\nVoter Info:\nWeight: %d\nVoted: %v\nVote Index: %d\nDelegate: %s\n", 
		weight, voted, voteIndex, delegate)
}
3.4 配置文件 conf/config.toml
XML 复制代码
[network]
peers=["127.0.0.1:20200"]  # 节点RPC端口

[chain]
chainId=1
groupId=1

[cryptoMaterial]
certPath="conf"
# caCertPath = "conf/ca.crt"
# sslCert = "conf/sdk.crt"
# sslKey = "conf/sdk.key"
# enSslCert = "conf/gm/gmensdk.crt"
# enSslKey = "conf/gm/gmensdk.key"

[account]
keyFile="conf/sdk.key"

[retry]
connectionTimeout=10000
reconnectRetryInterval=10000

四、测试脚本

bash 复制代码
# 编译Go程序
go mod init voting-demo
go mod tidy
go build -o voting-demo

# 运行程序
./voting-demo

五、Web3.js前端集成示例(可选)

javascript 复制代码
// web3-voting.js
const Web3 = require('web3');
const fs = require('fs');

class VotingDApp {
    constructor(config) {
        this.web3 = new Web3(config.rpcUrl);
        this.account = config.account;
        this.privateKey = config.privateKey;
        this.contractAddress = config.contractAddress;
        this.contractABI = JSON.parse(fs.readFileSync(config.abiPath, 'utf8'));
        this.contract = new this.web3.eth.Contract(
            this.contractABI, 
            this.contractAddress
        );
    }
    
    async giveRightToVote(voterAddress) {
        const tx = {
            from: this.account,
            to: this.contractAddress,
            gas: 3000000,
            data: this.contract.methods.giveRightToVote(voterAddress).encodeABI()
        };
        
        const signedTx = await this.web3.eth.accounts.signTransaction(
            tx, 
            this.privateKey
        );
        
        return await this.web3.eth.sendSignedTransaction(signedTx.rawTransaction);
    }
    
    async vote(proposalIndex) {
        const tx = {
            from: this.account,
            to: this.contractAddress,
            gas: 3000000,
            data: this.contract.methods.vote(proposalIndex).encodeABI()
        };
        
        const signedTx = await this.web3.eth.accounts.signTransaction(
            tx, 
            this.privateKey
        );
        
        return await this.web3.eth.sendSignedTransaction(signedTx.rawTransaction);
    }
    
    async getWinner() {
        return await this.contract.methods.winnerName().call();
    }
    
    async getProposalCount() {
        return await this.contract.methods.getProposalsCount().call();
    }
}

六、部署和测试步骤总结

  1. 1、启动FISCO BCOS节点

  2. 2、编译Solidity合约:生成ABI和BIN文件

  3. 3、配置Go SDK:设置证书和连接信息

  4. 4、部署合约:使用Go SDK部署投票合约

  5. 5、测试合约功能

    • 给投票者授权

    • 执行投票

    • 查询投票结果

    • 获取提案信息

注意事项

  1. 1、私钥安全:生产环境中不要硬编码私钥

  2. 2、交易确认:添加适当的交易确认等待逻辑

  3. 3、错误处理:完善错误处理和重试机制

  4. 4、Gas设置:根据合约复杂度调整Gas限制

  5. 5、链上数据:注意FISCO BCOS与以太坊的差异

这个完整的实现方案包含了从合约编写到Go SDK调用的全流程,您可以根据实际需求进行调整和扩展。

相关推荐
MQLYES2 小时前
20-ETH-难度调整
区块链
翔云1234562 小时前
golang中使用 sort.Interface 实现复杂多级排序
开发语言·后端·golang
ん贤2 小时前
自go-zero走进微服务
开发语言·后端·golang
源代码•宸17 小时前
Leetcode—404. 左叶子之和【简单】
经验分享·后端·算法·leetcode·职场和发展·golang·dfs
Grassto20 小时前
10 Go 是如何下载第三方包的?GOPROXY 与源码解析
后端·golang·go·go module
MQLYES21 小时前
17-ETH-交易树和收据树
区块链
终端域名21 小时前
区块链技术在金融领域的实际应用方法
金融·区块链
MQLYES21 小时前
16-ETH-状态树
区块链
源代码•宸21 小时前
Leetcode—513. 找树左下角的值【中等】
经验分享·算法·leetcode·面试·职场和发展·golang·dfs