hyperledger fabric 智能合约开发(五)------模仿以太坊ERC20规范的Hyperledger Fabric 实现Token通证案例

模仿以太坊ERC20规范的Hyperledger Fabric 实现Token通证案例

借用以太坊思维,将以太坊代币合约搬到 hyperledger 上,一样可以实现代币的功能,这个代币除了不能上交易所,基本满足我们替代积分系统的需求,下面是在超级账本上实现类似以太坊的代币转账功能。

合约实现代币转账,额度查询,增发代币,冻结账号,锁仓等等服务,功能与 ERC20 Token 相仿。合约实例化所有代币打入了 coinbase 账号,分发代币需要使用转账功能从 coinbase 想普通账号转账。普通账号消费可以在将代币转到 coinbase 账号中,这样就完成了代币流通,形成一个闭环。程序如下:

package main

import (

"encoding/json"

"fmt"

"strconv"

 

"github.com/hyperledger/fabric/core/chaincode/shim"

sc "github.com/hyperledger/fabric/protos/peer"

)

 

//默认合约数据结构

type SmartContract struct {

}

//积分数据结构定义

type Token struct {

Owner string `json:"Owner"`

TotalSupply int `json:"TotalSupply"`

TokenName string `json:"TokenName"`

TokenSymbol string `json:"TokenSymbol"`

BalanceOf map[string]int `json:"BalanceOf"`

}

//积分总额初始化函数

func (token *Token) initialSupply(){

token.BalanceOf[token.Owner] = token.TotalSupply;

}

//积分转移函数

func (token *Token) transfer (_from string, _to string, _value int){

if(token.BalanceOf[_from] >= _value){

token.BalanceOf[_from] -= _value;

token.BalanceOf[_to] += _value;

}

}

//积分余额函数

func (token *Token) balance (_from string) int{

return token.BalanceOf[_from]

}

//管理员账户积分部分销毁函数

func (token *Token) burn(_value int) {

if(token.BalanceOf[token.Owner] >= _value){

token.BalanceOf[token.Owner] -= _value;

token.TotalSupply -= _value;

}

}

//普通账户积分销毁函数

func (token *Token) burnFrom(_from string, _value int) {

if(token.BalanceOf[_from] >= _value){

token.BalanceOf[_from] -= _value;

token.TotalSupply -= _value;

}

}

//矿工函数,积分供应总额增加

func (token *Token) mint(_value int) {

 

token.BalanceOf[token.Owner] += _value;

token.TotalSupply += _value;

 

}

//合约默认初始化方法

func (s *SmartContract) Init(stub shim.ChaincodeStubInterface) sc.Response {

return shim.Success(nil)

}

//合约初始化账本函数

func (s *SmartContract) initLedger(stub shim.ChaincodeStubInterface, args []string) sc.Response {

//参数为3个,否则报错

if len(args) != 3 {

                return shim.Error("Incorrect number of arguments. Expecting 2")

        }

//参数1为积分标志,参数2为积分名字,参数3为积分总额

symbol:= args[0]

name  := args[1]

supply,_:= strconv.Atoi(args[2])

//token数据结构初始化

token := &Token{

Owner: "coinbase",

TotalSupply: supply,

TokenName: name,

TokenSymbol: symbol,

BalanceOf: map[string]int{}}

//调用积分总额初始化函数

token.initialSupply()

   //将token数据结构序列化写入区块链

tokenAsBytes, _ := json.Marshal(token)

err := stub.PutState(symbol, tokenAsBytes)

if err != nil {

return shim.Error(err.Error())

}

fmt.Printf("Init %s \n", string(tokenAsBytes))

 

return shim.Success(nil)

}

//积分交易函数

func (s *SmartContract) transferToken(stub shim.ChaincodeStubInterface, args []string) sc.Response {

   //参数不为4个报错

if len(args) != 4 {

return shim.Error("Incorrect number of arguments. Expecting 4")

}

_from := args[1]  //积分发生账户

_to := args[2]      //积分接受账户

_amount,_ := strconv.Atoi(args[3])   //积分转移数量

if(_amount <= 0){

return shim.Error("Incorrect number of amount")

}

    //获取积分发送账户的值数据

tokenAsBytes,err := stub.GetState(args[0])

if err != nil {

return shim.Error(err.Error())

}

fmt.Printf("transferToken - begin %s \n", string(tokenAsBytes))

    //新建积分数据结构

token := Token{}

    //将得到的二进制数据反序列化写入token数据结构

json.Unmarshal(tokenAsBytes, &token)

token.transfer(_from, _to, _amount) //调用积分转移函数

    //将转移后的token数据结构序列化为二进制数组

tokenAsBytes, err = json.Marshal(token)

if err != nil {

return shim.Error(err.Error())

}

err = stub.PutState(args[0], tokenAsBytes)  //写入区块链

if err != nil {

return shim.Error(err.Error())

}

fmt.Printf("transferToken - end %s \n", string(tokenAsBytes))

 

return shim.Success(nil)

}

//积分余额查询函数

func (s *SmartContract) balanceToken(stub shim.ChaincodeStubInterface, args []string) sc.Response {

//参数不为2报错

if len(args) != 2 {

return shim.Error("Incorrect number of arguments. Expecting 2")

}

//获取参数0对应的值

tokenAsBytes,err := stub.GetState(args[0])

if err != nil {

return shim.Error(err.Error())

}

token := Token{} //新建token数据结构

   //将得到的值反序列化写入token数据结构

json.Unmarshal(tokenAsBytes, &token)

amount := token.balance(args[1])

value := strconv.Itoa(amount)

fmt.Printf("%s balance is %s \n", args[1], value)

//jsonVal, _ := json.Marshal(string(value))

return shim.Success([]byte(value))

}

//合约调用函数方法

func (s *SmartContract) Invoke(stub shim.ChaincodeStubInterface) sc.Response {

   //获取要调用的智能合约的函数和参数

function, args := stub.GetFunctionAndParameters()

//根据参数匹配合适的调用函数

if function == "balanceToken" {

return s.balanceToken(stub, args)

} else if function == "initLedger" {

return s.initLedger(stub, args)

} else if function == "transferToken" {

return s.transferToken(stub, args)

}

 

return shim.Error("Invalid Smart Contract function name.")

}

//合约执行开始方法,主要功能仅与单元测试模式有关。

func main() {

    //创建一个新的合约,合约开始执行

err := shim.Start(new(SmartContract))

if err != nil {

fmt.Printf("Error creating new Smart Contract: %s", err)

}

}

这个合约用户可以创建多套代币,Args":["Token" 的第一参数 Token就是代币名称,第二参数为初始化供应总额}

peer chaincode invoke -C myc -n token -c '{"function":"initLedger","Args":["Apple","水果币","1000000"]}'

peer chaincode invoke -C myc -n token -c '{"function":"initLedger","Args":["Token","蔬菜币","1000000"]}'

peer chaincode invoke -C myc -n token -c '{"function":"initLedger","Args":["Oil","粮油币","1000000"]}'

这个方案仍有不足之处,作者还不清楚如果用户上线是多少,达到临界值后,Hyperledger Fabric 无法在提供服务。

可能 chaincode_example02 做法更靠谱,就是不用 map 保存数据,将每个用户存储在 State 数据上。这里需要创建多套代币,所以使用了一个key 来存储所有账号。如果像 chaincode_example02 那样就需要部署多个 chaincode 在 channel 中。管理起来比较复杂。

合约测试:

[root@localhost fabric-samples]# cd chaincode-docker-devmode/

[root@localhost chaincode-docker-devmode]# docker-compose up -d

[root@localhost chaincode-docker-devmode]# docker exec -it cli bash

root@765cbcd51fd7:/opt/gopath/src/chaincode#

CORE_PEER_ADDRESS=peer:7052

CORE_CHAINCODE_ID_NAME=token:1.0 ./chaincode/chaincode/token/token

peer chaincode install -n token -v 1.0 -p chaincodedev/chaincode/chaincode/token

peer chaincode instantiate -C myc -n token -v 1.0 -c '{"Args":[""]}' -P "OR ('Org1MSP.member','Org2MSP.member')"

peer chaincode instantiate -C myc -n token -v 1.0 -c '{"Args":["init"]}'

sleep 10

peer chaincode invoke -C myc -n token -c '{"function":"initLedger","Args":["Token","Netkiller Coin","1000000"]}'

peer chaincode invoke -C myc -n token -c '{"function":"transferToken","Args":["Token","coinbase","netkiller","100"]}'

peer chaincode invoke -C myc -n token -c '{"function":"balanceToken","Args":["Token","netkiller"]}'

peer chaincode invoke -C myc -n token -c '{"function":"transferToken","Args":["Token","netkiller","jerry","100"]}'

peer chaincode query -C myc -n token -c '{"function":"balanceToken","Args":["Token","netkiller"]}'

测试不存在账号转账:

peer chaincode invoke -C myc -n token -c '{"function":"transferToken","Args":["Token","neo","netkiller","100"]}'

以太坊和超级账本各有优势,虽然超级账本的Token功能无法和以太坊相比,但是使用超级账本实现的Token交易不用矿工费。同事超级账本还有一个优势,就是可以在合约中调用另一个合约,这样一来可以做出很多复杂的需求,例如我们在订票的合约中,就可以直接从Token合约中直接扣款。

希望大家关注我的微信公众号,有疑问可以后台留言。

你可能感兴趣的:(hyperledger,Go,区块链(Blockchain))