【转载请标明出处】https://blog.csdn.net/qq_25870633/article/details/82931782
{
"config": {
"chainId": 15,
"homesteadBlock": 0,
"eip155Block": 0,
"eip158Block": 0
},
"coinbase" : "0x0000000000000000000000000000000000000000",
"difficulty" : "0x40000",
"extraData" : "",
"gasLimit" : "0xffffffff",
"nonce" : "0x0000000000000042",
"mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp" : "0x00",
"alloc": { }
}
来,我们来说一说,这个创世块的配置文件中的各项分别是些什么东西:
参数名称 | 参数描述 |
---|---|
mixhash | 与nonce配合用于挖矿,由上一个区块的一部分生成的hash。注意他和nonce的设置需要满足以太坊的Yellow paper, 4.3.4. Block Header Validity, (44)章节所描述的条件。 |
nonce | nonce就是一个64位随机数,用于挖矿,注意他和mixhash的设置需要满足以太坊的Yellow paper, 4.3.4. Block Header Validity, (44)章节所描述的条件。 |
difficulty | 设置当前区块的难度,如果难度过大,cpu挖矿就很难,这里设置较小难度 |
alloc | 用来预置账号以及账号的以太币数量,因为私有链挖矿比较容易,所以我们不需要预置有币的账号,需要的时候自己创建即可以。 |
coinbase | 矿工的账号,随便填 |
timestamp | 设置创世块的时间戳 |
parentHash | 上一个区块的hash值,因为是创世块,所以这个值是0 |
extraData | 附加信息,随便填,可以填你的个性信息 |
gasLimit | 该值设置对GAS的消耗总量限制,用来限制区块能包含的交易信息总和,因为我们是私有链,所以填最大。 |
其中 config 选项中的各项说明:
chainId | 标识当前链并用于重放保护。您应该将其设置为专用链的唯一值。主要用于EIP155 中 |
homesteadBlock | 当前chain 不会被切换成 Homestead 版本,所以应当设置为 0 。 |
eip155Block | 当前chain 不会因 硬分叉 而改变,所以应当设置为 0 。 |
eip158Block | 同上。 |
准备好创世区块json配置文件后,需要初始化区块链,将上面的创世区块信息写入到区块链中。首先要新建一个目录 data0 【注意: 可以使任意命名的目录哦,比如:我是用了 genesis-block】用来存放区块链数据【其实,这个目录data0就相当于一个根节点。当我们基于genesis.json生成根节点后,其他人就可以来连接此根节点,从而能进行交易】。
在命令行输入:【注意需要在 根目录输入】
geth --datadir genesis-block init genesis.json
上述命令的意思是,读取genesis.json文件,根据其中的内容,将创世区块写入到区块链中。当我们看到有 Successfully wrote genesis state 等日志输出时,代表初始化私有区块链成功。
我们进入 自定义的 genesis-block 目录,可以看到如下信息:
其中,geth/chaindata 中存放的是区块数据,geth/lightchaindata 是 轻节点 所存放的区块头信息,keystore 中存放的是账户数据。
在初始化完成后,我们需要启动私有链节点,在命令行输入:【注意需要在 根目录输入】
geth --datadir genesis-block --networkid 1108 console
即可启动本地私有链节点。
然后,我们看到当前目录的结构是这样纸的:
我们这里也顺便讲一讲 geth 命令的一些参数:参考自 https://www.cnblogs.com/tinyxiong/p/7918706.html
geth命令的用法: geth [选项] 命令 [命令选项] [参数…] 。
account 管理账户
attach 启动交互式JavaScript环境(连接到节点)
bug 上报bug Issues
console 启动交互式JavaScript环境
copydb 从文件夹创建本地链
dump Dump(分析)一个特定的块存储
dumpconfig 显示配置值
export 导出区块链到文件
import 导入一个区块链文件
init 启动并初始化一个新的创世纪块
js 执行指定的JavaScript文件(多个)
license 显示许可信息
makecache 生成ethash验证缓存(用于测试)
makedag 生成ethash 挖矿DAG(用于测试)
monitor 监控和可视化节点指标
removedb 删除区块链和状态数据库
version 打印版本号
wallet 管理Ethereum预售钱包
help,h 显示一个命令或帮助一个命令列表
--config value TOML 配置文件
--datadir “xxx” 数据库和keystore密钥的数据目录
--keystore keystore存放目录(默认在datadir内)
--nousb 禁用监控和管理USB硬件钱包
--networkid value 网络标识符【整型, 1=Frontier (主网), 2=Morden (弃用), 3=Ropsten, 4=Rinkeby】(默认: 1)
--testnet Ropsten网络:预先配置的POW(proof-of-work)测试网络
--rinkeby Rinkeby网络: 预先配置的POA(proof-of-authority)测试网络
--syncmode "fast" 同步模式 ("fast", "full", or "light") 对应代码中的 syncmode 哦
--ethstats value 上报ethstats service URL (nodename:secret@host:port)
--identity value 自定义节点名
--lightserv value 允许LES请求时间最大百分比(0 – 90)(默认值:0)
--lightpeers value 最大LES client peers数量(默认值:20)
--lightkdf 在KDF强度消费时降低key-derivation RAM&CPU使用
--dev 使用POA共识网络,默认预分配一个开发者账户并且会自动开启挖矿。
--dev.period value 开发者模式下挖矿周期 (0 = 仅在交易时) (默认: 0)
--ethash.cachedir ethash验证缓存目录(默认 = datadir目录内)
--ethash.cachesinmem value 在内存保存的最近的ethash缓存个数 (每个缓存16MB ) (默认: 2)
--ethash.cachesondisk value 在磁盘保存的最近的ethash缓存个数 (每个缓存16MB) (默认: 3)
--ethash.dagdir "" 存ethash DAGs目录 (默认 = 用户hom目录)
--ethash.dagsinmem value 在内存保存的最近的ethash DAGs 个数 (每个1GB以上) (默认: 1)
--ethash.dagsondisk value
--txpool.nolocals 为本地提交交易禁用价格豁免
--txpool.journal value 本地交易的磁盘日志:用于节点重启 (默认: "transactions.rlp")
--txpool.rejournal value 重新生成本地交易日志的时间间隔 (默认: 1小时)
--txpool.pricelimit value 加入交易池的最小的gas价格限制(默认: 1)
--txpool.pricebump value 价格波动百分比(相对之前已有交易) (默认: 10)
--txpool.accountslots value 每个帐户保证可执行的最少交易槽数量 (默认: 16)
--txpool.globalslots value 所有帐户可执行的最大交易槽数量 (默认: 4096)
--txpool.accountqueue value 每个帐户允许的最多非可执行交易槽数量 (默认: 64)
--txpool.globalqueue value 所有帐户非可执行交易最大槽数量 (默认: 1024)
--txpool.lifetime value 非可执行交易最大入队时间(默认: 3小时)
--cache value 分配给内部缓存的内存MB数量,缓存值(最低16 mb /数据库强制要求)(默认:128)
--trie-cache-gens value 保持在内存中产生的trie node数量(默认:120)
--unlock value 需解锁账户用逗号分隔
--password value 用于非交互式密码输入的密码文件
--rpc 启用HTTP-RPC服务器
--rpcaddr value HTTP-RPC服务器接口地址(默认值:“localhost”)
--rpcport value HTTP-RPC服务器监听端口(默认值:8545)
--rpcapi value 基于HTTP-RPC接口提供的API
--ws 启用WS-RPC服务器
--wsaddr value WS-RPC服务器监听接口地址(默认值:“localhost”)
--wsport value WS-RPC服务器监听端口(默认值:8546)
--wsapi value 基于WS-RPC的接口提供的API
--wsorigins value websockets请求允许的源
--ipcdisable 禁用IPC-RPC服务器
--ipcpath 包含在datadir里的IPC socket/pipe文件名(转义过的显式路径)
--rpccorsdomain value 允许跨域请求的域名列表(逗号分隔)(浏览器强制)
--jspath loadScript JavaScript加载脚本的根路径(默认值:“.”)
--exec value 执行JavaScript语句(只能结合console/attach使用)
--preload value 预加载到控制台的JavaScript文件列表(逗号分隔)
--bootnodes value 用于P2P发现引导的enode urls(逗号分隔)(对于light servers用v4+v5代替)
--bootnodesv4 value 用于P2P v4发现引导的enode urls(逗号分隔) (light server, 全节点)
--bootnodesv5 value 用于P2P v5发现引导的enode urls(逗号分隔) (light server, 轻节点)
--port value 网卡监听端口(默认值:30303)
--maxpeers value 最大的网络节点数量(如果设置为0,网络将被禁用)(默认值:25)
--maxpendpeers value 最大尝试连接的数量(如果设置为0,则将使用默认值)(默认值:0)
--nat value NAT端口映射机制 (any|none|upnp|pmp|extip:) (默认: “any”)
--nodiscover 禁用节点发现机制(手动添加节点)
--v5disc 启用实验性的RLPx V5(Topic发现)机制
--nodekey value P2P节点密钥文件
--nodekeyhex value 十六进制的P2P节点密钥(用于测试)
--mine 打开挖矿
--minerthreads value 挖矿使用的CPU线程数量(默认值:8)
--etherbase value 挖矿奖励地址(默认=第一个创建的帐户)(默认值:“0”)
--targetgaslimit value 目标gas限制:设置最低gas限制(低于这个不会被挖?) (默认值:“4712388”)
--gasprice value 挖矿接受交易的最低gas价格
--extradata value 矿工设置的额外块数据(默认=client version)
--gpoblocks value 用于检查gas价格的最近块的个数 (默认: 10)
--gpopercentile value 建议gas价参考最近交易的gas价的百分位数,(默认: 50)
--vmdebug 记录VM及合约调试信息
--metrics 启用metrics收集和报告
--fakepow 禁用proof-of-work验证
--verbosity value 日志详细度:0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=detail (default: 3)
--vmodule value 每个模块详细度:以 =的逗号分隔列表 (比如 eth/*=6,p2p=5)
--backtrace value 请求特定日志记录堆栈跟踪 (比如 “block.go:271”)
--debug 突出显示调用位置日志(文件名及行号)
--pprof 启用pprof HTTP服务器
--pprofaddr value pprof HTTP服务器监听接口(默认值:127.0.0.1)
--pprofport value pprof HTTP服务器监听端口(默认值:6060)
--memprofilerate value 按指定频率打开memory profiling (默认:524288)
--blockprofilerate value 按指定频率打开block profiling (默认值:0)
--cpuprofile value 将CPU profile写入指定文件
--trace value 将execution trace写入指定文件
--shh 启用Whisper
--shh.maxmessagesize value 可接受的最大的消息大小 (默认值: 1048576)
--shh.pow value 可接受的最小的POW (默认值: 0.2)
--fast 开启快速同步
--light 启用轻客户端模式
–help, -h 显示帮助
这时候我们就可以在geth的console 输入 json-rpc的api来操作 geth节点了。好了,现在我们先来创建下两个账户:
我们可以看到当前用户的余额分别是 0 【这里说的余额是指 ether】。
好了,上述命令行基本是通过 js 的操作api操作的,目前私有节点已经被启动了,下面我们来进入本次文章的重点,用go项目和这个 geth节点进行交互,及使用abigen 来把 *.sol 文件编译成 *.go 文件,来在go项目中操作合约实例。
首先,我们来编写一个智能合约文件:
pragma solidity ^0.4.16;
contract Token{
uint256 public totalSupply;
function balanceOf(address _owner) public constant returns (uint256 balance);
function transfer(address _to, uint256 _value) public returns (bool success);
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
function approve(address _spender, uint256 _value) public returns (bool success);
function allowance(address _owner, address _spender) public constant returns (uint256 remaining);
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}
/**
自定义的GAVC代币
*/
contract GavinToken is Token {
/**
代币名称,例如"Gavin token"
*/
string public name;
/**
返回token使用的小数点后几位。比如如果设置为3,就是支持0.001表示.
*/
uint8 public decimals;
/**
token简称, GAVC
*/
string public symbol;
mapping (address => uint256) balances;
mapping (address => mapping (address => uint256)) allowed;
/**
构造方法
*/
function GavinToken(uint256 _initialAmount, string _tokenName, uint8 _decimalUnits, string _tokenSymbol) public {
// 设置初始总量
totalSupply = _initialAmount * 10 ** uint256(_decimalUnits);
/**
初始token数量给予消息发送者,因为是构造函数,所以这里也是合约的创建者
*/
balances[msg.sender] = totalSupply;
name = _tokenName;
decimals = _decimalUnits;
symbol = _tokenSymbol;
}
function transfer(address _to, uint256 _value) public returns (bool success) {
//默认totalSupply 不会超过最大值 (2^256 - 1).
//如果随着时间的推移将会有新的token生成,则可以用下面这句避免溢出的异常
require(balances[msg.sender] >= _value && balances[_to] + _value > balances[_to]);
require(_to != 0x0);
//从消息发送者账户中减去token数量_value
balances[msg.sender] -= _value;
//往接收账户增加token数量_value
balances[_to] += _value;
//触发转币交易事件
Transfer(msg.sender, _to, _value);
return true;
}
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
require(balances[_from] >= _value && allowed[_from][msg.sender] >= _value);
//接收账户增加token数量_value
balances[_to] += _value;
//支出账户_from减去token数量_value
balances[_from] -= _value;
//消息发送者可以从账户_from中转出的数量减少_value
allowed[_from][msg.sender] -= _value;
//触发转币交易事件
Transfer(_from, _to, _value);
return true;
}
function balanceOf(address _owner) public constant returns (uint256 balance) {
return balances[_owner];
}
function approve(address _spender, uint256 _value) public returns (bool success) {
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
return true;
}
function allowance(address _owner, address _spender) public constant returns (uint256 remaining) {
//允许_spender从_owner中转出的token数
return allowed[_owner][_spender];
}
}
然后我们去到go-ethereum 源码的 go-ethereum/cmd/abigen 目录下,执行 go install 编译出 abigen 可执行文件,且加入到 GOPATH/bin 目录中。
然后,我们使用 solc 先分别把*sol 编译成 abi 【应用程序二进制接口规范】及bin【十六进制字节码】文件:
solc GavinToken.sol -o filedir --abi
solc GavinToken.sol -o filedir --bin
然后,我们再根据abi及bin文件使用 abigen 编译出go文件:
abigen --abi filedir/GavinToken_sol_GavinToken.abi --bin filedir/GavinToken_sol_GavinToken.bin --pkg mytoken --out gavinToken.go
在这里需要注意:
【注意】:网上的sol直接生成go文件:abigen --sol GavinToken.sol --pkg mytoken --out gavinToken.go 是有问题的可能会报一下错误:
Failed to build Solidity contract: solc: exit status 1
Invalid option selected, must specify either --bin or --abi
或者
Failed to build Solidity contract: exit status 7
下面我们看一看如何使用ethClient 及 生成的 tokengo 文件:【核心代码如下所示,完整代码放置我的github了,go的框架是使用了 iris 搭建的】
package service
import (
"blockclient/src/server/entity"
"blockclient/src/server/common/stacode"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
myCommon "blockclient/src/server/common"
"log"
"strings"
"context"
"math/big"
"fmt"
"time"
"bytes"
"blockclient/src/server/tokens"
//"encoding/json"
)
type EthService struct {
}
// get balance of a account by account's address
func (es *EthService) GetBalanceByAddress(addresses, num string) *entity.Result {
if "" == strings.TrimSpace(addresses) {
return errcode.REQUEST_PARAM_ERR.Result(nil)
}
//balance, err := myCommon.EthConn.BalanceAt(context.Background(), common.HexToAddress(address), big.NewInt(number))
block, err := myCommon.EthConn.BlockByNumber(context.Background(), nil)
if nil != err {
log.Println("get blockNumber err:", err)
return errcode.SYSTEMBUSY_ERROR.Result(nil)
}else {
log.Println("blockNumber:", block.Number().String())
}
resMap := make(map[string]interface{}, 0)
for _, address := range strings.Split(addresses, ",") {
balance, err := myCommon.EthConn.BalanceAt(context.Background(), common.HexToAddress(address), nil)
if nil != err {
log.Println("Failed to query ether balance by address: %v", err)
log.Println("Failed to query ether balance by address: ", err.Error())
return errcode.SYSTEMBUSY_ERROR.Result(nil)
}
resMap[address] = balance
}
return errcode.SUCCESS.Result(resMap)
}
func (es *EthService) DeployContract(total int64, decimals uint8, name, symbol, key, passphrase string) *entity.Result {
// 【说明】
// 安装solidity编译器solc (为 solcjs): sudo npm install -g solc
// 设置链接至 /usr/bin/中 (虽然可以用solcjs已经设置在 /usr/local/bin中了,但是有些地方需要以solc 启动,如abigen就是):sudo ln -s /usr/local/lib/node_modules/solc/solcjs /usr/bin/solc
// 查看下是否可以以solc访问: solc --help
// 进入到项目中的 *sol 文件所在目录: cd ~/go-workspace/src/blockclient/src/server/tokens/
// 生成abi文件: solc GavinToken.sol -o filedir --abi
// 生成bin文件: solc GavinToken.sol -o filedir --bin
// 生成go文件: abigen --abi filedir/GavinToken_sol_GavinToken.abi --bin filedir/GavinToken_sol_GavinToken.bin --pkg mytoken --out gavinToken.go
// 【注意】:网上的sol直接生成go文件是有问题的可能会报一下错误: abigen --sol GavinToken.sol --pkg mytoken --out gavinToken.go
// Failed to build Solidity contract: solc: exit status 1
// Invalid option selected, must specify either --bin or --abi
// 或者
// Failed to build Solidity contract: exit status 7
if "" == strings.TrimSpace(passphrase) {
return errcode.REQUEST_PARAM_ERR.ResultWithMsg("must have pwd")
}
auth, err := bind.NewTransactor(strings.NewReader(key), passphrase)
if nil != err {
log.Panic("Failed to create authorized transactor: %v", err)
}
// 发布合约返回 合约地址、 当前交易信息、 和当前的合约实例(操作合约abi用)
address, tx, token, err := mytoken.DeployMytoken(auth, myCommon.EthConn, big.NewInt(total), name, decimals, symbol)
if nil != err {
log.Panic("Failed to deploy new token contract: %v", err)
}
fmt.Printf("Contract pending deploy: 0x%x\n", address)
fmt.Printf("Transaction waiting to be mined: 0x%x\n\n", tx.Hash())
startTime := time.Now()
fmt.Printf("TX start @:%s", time.Now())
// 等待挖矿确认
addressAfterMined, err := bind.WaitDeployed(context.Background(), myCommon.EthConn, tx)
if nil != err {
log.Panic("failed to deploy contact when mining :%v", err)
}
fmt.Printf("tx mining take time:%s\n", time.Now().Sub(startTime))
if bytes.Compare(address.Bytes(), addressAfterMined.Bytes()) != 0 {
log.Panic("mined address :%s,before mined address:%s", addressAfterMined, address)
}
// token的Name
nameRes, err := token.Name(&bind.CallOpts{Pending: true})
if nil != err {
log.Panic("Failed to retrieve pending name: %v", err)
}
totalRes, err := token.TotalSupply(&bind.CallOpts{Pending: true})
if nil != err {
log.Panic("Failed to retrieve pending total: %v", err)
}
symbolRes, err := token.Symbol(&bind.CallOpts{Pending: true})
if nil != err {
log.Panic("Failed to retrieve pending symbol: %v", err)
}
decimalsRes, err := token.Decimals(&bind.CallOpts{Pending: true})
if nil != err {
log.Panic("Failed to retrieve pending decimals: %v", err)
}
res := make(map[string]interface{}, 0)
res["txHAsh"] = tx.Hash()
res["contractAddress"] = address.String()
res["Name"] = nameRes
res["total"] = totalRes.String()
res["symbol"] = symbolRes
res["decimals"] = decimalsRes
return errcode.SUCCESS.Result(res)
}
func (es *EthService) TokenTransfer(contractAddress, to, key, pwd string, value *big.Int) *entity.Result {
//go es.SubcribeEvents(contractAddress, to)
token, err := mytoken.NewMytoken(common.HexToAddress(contractAddress), myCommon.EthConn)
if err != nil {
log.Panic("Failed to instantiate a Token contract: %v", err)
}
toAddress := common.HexToAddress(to)
toPreval, _ := token.BalanceOf(nil, toAddress)
// Create an authorized
auth, err := bind.NewTransactor(strings.NewReader(key), pwd)
if err != nil {
log.Panic("Failed to create authorized transactor: %v", err)
}
// Create a transfer
tx, err := token.Transfer(auth, toAddress, value)
if err != nil {
log.Panic("Failed to request token transfer: %v", err)
}
//var keyStorage entity.KeyStorage
//json.Unmarshal([]byte(key), &keyStorage)
//from := keyStorage.Address
// start-up transfer event subcribe
fmt.Printf("Transaction waiting to be mined: 0x%x\n\n", tx.Hash())
startTime := time.Now()
fmt.Printf("TX start @:%s", time.Now())
// waiting mining
receipt, err := bind.WaitMined(context.Background(), myCommon.EthConn, tx)
if err != nil {
log.Panic("tx mining error:%v\n", err)
}
fmt.Printf("tx mining take time:%s\n", time.Now().Sub(startTime))
val, _ := token.BalanceOf(nil, toAddress)
res := make(map[string]interface{}, 0)
res["toPreval"] = toPreval
res["tx"] = tx.Hash()
res["toVal"] = val
res["receipt.TxHash"] = receipt.TxHash
recp, _ := receipt.MarshalJSON()
res["receiptJson"] = string(recp)
return errcode.SUCCESS.Result(res)
}
func (es *EthService) QueryTokenBalance(contractAddress, eoas string) *entity.Result {
if "" == strings.TrimSpace(contractAddress) || "" == strings.TrimSpace(eoas) {
return errcode.REQUEST_PARAM_ERR.ResultWithMsg("contract and from must is not empty")
}
token, err := mytoken.NewMytoken(common.HexToAddress(contractAddress), myCommon.EthConn)
if err != nil {
log.Panic("Failed to instantiate a Token contract: %v", err)
}
resMap := make(map[string]interface{}, 0)
for _, from := range strings.Split(eoas, ",") {
val, _ := token.BalanceOf(nil, common.HexToAddress(from))
resMap[from] = val.String()
}
return errcode.SUCCESS.Result(resMap)
}
func (es *EthService) SubcribeEvents(contractAddress, to string) {
filter, err := mytoken.NewMytokenFilterer(common.HexToAddress(contractAddress), myCommon.EthConn)
ch := make(chan *mytoken.MytokenTransfer, 10)
//sub, err := filter.WatchTransfer(&bind.WatchOpts{}, ch, []common.Address{common.HexToAddress(from)}, []common.Address{common.HexToAddress(to)})
sub, err := filter.WatchTransfer(nil, ch, nil, []common.Address{common.HexToAddress(to)})
if err != nil {
log.Panic("watch transfer err %s", err)
}
go func() {
for {
select {
case <-sub.Err():
return
case e := <-ch:
log.Printf("new transfer event from %s to %s value=%s,at %d",
e.From.String(), e.To.String(), e.Value, e.Raw.BlockNumber)
}
}
}()
}
下面我们骚微来分析下代码。
首先,我这里只写了4个简单的小接口,分别是,查询账户的ethe余额、查询账户的token余额、发起一笔token交易、发布一个合约等四个接口的demo。
由于我们需要操作合约,所以我们不止是需要使用到ethclient还需要使用到abigen先把sol文件编译成go合约文件加到项目中以便操作合约的abi。关于abigen的使用上面我们已经有说明了。
首先,我们看下我们的router层:
可以看出,是只定义了四个接口,然后我们再看看controller层:
package controller
import (
"gopkg.in/kataras/iris.v5"
"blockclient/src/server/service"
"encoding/json"
"blockclient/src/server/entity"
"blockclient/src/server/common/stacode"
"math/big"
)
type EthController struct {
}
var ethService = service.EthService{}
// query account's ether balance
func (ec *EthController) GetBlanceByAddress(ctx *iris.Context) {
ctx.JSON(iris.StatusOK, ethService.GetBalanceByAddress(ctx.URLParam("addresses"), ctx.URLParam("num")))
}
// deploy a contract
func (ec *EthController) DeployContract(ctx *iris.Context){
var res entity.DeployEntity
if err := json.Unmarshal(ctx.PostBody(), &res); nil != err {
ctx.JSON(iris.StatusOK, errcode.REQUEST_PARAM_ERR.Result(err))
return
}
key, err := json.Marshal(res.Key)
if nil != err {
ctx.JSON(iris.StatusOK, errcode.REQUEST_PARAM_ERR.Result("parse key err: " + err.Error()))
return
}
ctx.JSON(iris.StatusOK, ethService.DeployContract(res.Total, res.Decimals, res.Name, res.Symbol, string(key), res.Pwd))
}
func (ec *EthController) TokenTransfer (ctx *iris.Context) {
var res entity.ToekenTransferEntity
if err := json.Unmarshal(ctx.PostBody(), &res); nil != err {
ctx.JSON(iris.StatusOK, errcode.REQUEST_PARAM_ERR.Result(err))
return
}
key, err := json.Marshal(res.Key)
if nil != err {
ctx.JSON(iris.StatusOK, errcode.REQUEST_PARAM_ERR.Result("parse key err: " + err.Error()))
return
}
ctx.JSON(iris.StatusOK, ethService.TokenTransfer(res.Address, res.To, string(key), res.Pwd, big.NewInt(int64(res.Value))))
}
func (ec *EthController) QueryTokenBalance (ctx *iris.Context) {
ctx.JSON(iris.StatusOK, ethService.QueryTokenBalance(ctx.URLParam("contract"), ctx.URLParam("eoas")))
}
其中,有部分controller使用到了自定义的一些请求入参的entity:
package entity
type DeployEntity struct {
Decimals uint8 `json:"decimals"`
Total int64 `json:"total"`
Name string `json:"name"`
Symbol string `json:"symbol"`
// passphrase
Pwd string `json:"pwd"`
Key KeyStorage `json:"key"`
}
type ToekenTransferEntity struct {
To string `json:"to"`
Pwd string `json:"pwd"`
Address string `json:"address"`
Value int `json:"value"`
Key KeyStorage `json:"key"`
}
// Keystorage context
type KeyStorage struct{
Address string `json:"address"`
Crypto struct{
Cipher string `json:"cipher"`
Ciphertext string `json:"ciphertext"`
Cipherparams struct{
Iv string `json:"iv"`
} `json:"cipherparams"`
Kdf string `json:"kdf"`
Kdfparams struct{
Dklen int `json:"dklen"`
N int `json:"n"`
P int `json:"p"`
R int `json:"r"`
Salt string `json:"salt"`
} `json:"kdfparams"`
Mac string `json:"mac"`
} `json:"crypto"`
Id string `json:"id"`
Version int `json:"version"`
}
里面有些请求entity要求我们传入 keystorage里面的内容,对应到Key字段。service部分就不做解释了,自己看就ok!下面我们来看看本地使用Postman来测试接口:
好了,以上我们就可以使用原生的 geth的RPC API 和geth节点交互查询ether余额、当前区块数目、区块的Hash值等等,也可以操作某个合约中的方法【其实每次调用合约的时候都需要我们入参合约的地址来实例化合约,而合约的go文件其实就相当于合约的abi了,这和在web3.js里面操作合约时需知道合约的地址+合约的abi是一个道理的】,那么,今天我就写到这里了,祝大家国庆剩下的时间继续耍嗨!