fabric2.3链码对比1.4链码小记

最近实验室的项目要部署到fabric2.0以上版本,之前写的都是1.4的链码,现在看2.0版本的链码还是有些不一样的,主要是链码api改了:

前提:如果想在fabric2.0以上环境中还是想用shim和peerAPI的话:也就是:这里记录一下我们在1.4链码中是需要初始化的,因为我们有些初始化的操作是写在Init方法中的,但是呢在2.0以上版本取消了链码的初始化操作,所以当我们在2.0以上环境中需要延续1.4的Init方法的时候,我们需要在Invoke方法中也把Init方法加进去:实例:

Init方法:

func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {
	// Get the args from the transaction proposal
	_, args := stub.GetFunctionAndParameters()
	if len(args) != 2 {
		return shim.Error("Incorrect arguments. Expecting a key and a value")
	}
	// Set up any variables or assets here by calling stub.PutState()
	// We store the key and the value on the ledger
	err := stub.PutState(args[0], []byte(args[1]))
	if err != nil {
		return shim.Error(fmt.Sprintf("Failed to create asset: %s", args[0]))
	}
	return shim.Success([]byte("ok....."))
}

Invoke方法:

func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
	// Extract the function and args from the transaction proposal
	fn, args := stub.GetFunctionAndParameters()
	var result string
	var err error
	if fn == "set" {
		result, err = set(stub, args)
	} else if fn == "get"{ // assume 'get' even if fn is nil
		result, err = get(stub, args)
	} else if fn == "Init"{ // assume 'get' even if fn is nil
		return t.Init(stub)
	}
	if err != nil {
		return shim.Error(err.Error())
	}

	// Return the result as success payload
	return shim.Success([]byte(result))
}

 

这里看到,我们已经把Init方法加入了Invoke函数中了!

在fabric2.3环境运行:

注意一下,2.0链码的-c后面的格式发生了新变化:

-c '{"Args":["PutPublicInfordiy","PublicKey"]}'等价于 //这里默认第一个参数就是函数名,后面才是开始的参数列表
-c '{"function":"PutPublicInfordiy","Args":["PublicKey"]}'//这里很明显,函数名和参数已经隔开了,更加的清晰化。

这两种链格式效果是一样的!可以自己选择用哪种!

下面是fabric2.3提供的新的链码接口:contractapi

下面实例这个链码:

package chaincode

import (
    "encoding/json"
    "fmt"

    "github.com/hyperledger/fabric-contract-api-go/contractapi"
)

// SmartContract provides functions for managing an Asset
type SmartContract struct {
    contractapi.Contract
}

// Asset describes basic details of what makes up a simple asset
type Asset struct {
    ID             string `json:"ID"`
    Color          string `json:"color"`
    Size           int    `json:"size"`
    Owner          string `json:"owner"`
    AppraisedValue int    `json:"appraisedValue"`
}

// InitLedger adds a base set of assets to the ledger
func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {
    assets := []Asset{
        {ID: "asset1", Color: "blue", Size: 5, Owner: "Tomoko", AppraisedValue: 300},
        {ID: "asset2", Color: "red", Size: 5, Owner: "Brad", AppraisedValue: 400},
        {ID: "asset3", Color: "green", Size: 10, Owner: "Jin Soo", AppraisedValue: 500},
        {ID: "asset4", Color: "yellow", Size: 10, Owner: "Max", AppraisedValue: 600},
        {ID: "asset5", Color: "black", Size: 15, Owner: "Adriana", AppraisedValue: 700},
        {ID: "asset6", Color: "white", Size: 15, Owner: "Michel", AppraisedValue: 800},
    }

    for _, asset := range assets {
        assetJSON, err := json.Marshal(asset)
        if err != nil {
            return err
        }

        err = ctx.GetStub().PutState(asset.ID, assetJSON)
        if err != nil {
            return fmt.Errorf("failed to put to world state. %v", err)
        }
    }

    return nil
}

// CreateAsset issues a new asset to the world state with given details.
func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error {
    exists, err := s.AssetExists(ctx, id)
    if err != nil {
        return err
    }
    if exists {
        return fmt.Errorf("the asset %s already exists", id)
    }

    asset := Asset{
        ID:             id,
        Color:          color,
        Size:           size,
        Owner:          owner,
        AppraisedValue: appraisedValue,
    }
    assetJSON, err := json.Marshal(asset)
    if err != nil {
        return err
    }

    return ctx.GetStub().PutState(id, assetJSON)
}

// ReadAsset returns the asset stored in the world state with given id.
func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, id string) (*Asset, error) {
    assetJSON, err := ctx.GetStub().GetState(id)
    if err != nil {
        return nil, fmt.Errorf("failed to read from world state: %v", err)
    }
    if assetJSON == nil {
        return nil, fmt.Errorf("the asset %s does not exist", id)
    }

    var asset Asset
    err = json.Unmarshal(assetJSON, &asset)
    if err != nil {
        return nil, err
    }

    return &asset, nil
}

// UpdateAsset updates an existing asset in the world state with provided parameters.
func (s *SmartContract) UpdateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error {
    exists, err := s.AssetExists(ctx, id)
    if err != nil {
        return err
    }
    if !exists {
        return fmt.Errorf("the asset %s does not exist", id)
    }

    // overwriting original asset with new asset
    asset := Asset{
        ID:             id,
        Color:          color,
        Size:           size,
        Owner:          owner,
        AppraisedValue: appraisedValue,
    }
    assetJSON, err := json.Marshal(asset)
    if err != nil {
        return err
    }

    return ctx.GetStub().PutState(id, assetJSON)
}

// DeleteAsset deletes an given asset from the world state.
func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface, id string) error {
    exists, err := s.AssetExists(ctx, id)
    if err != nil {
        return err
    }
    if !exists {
        return fmt.Errorf("the asset %s does not exist", id)
    }

    return ctx.GetStub().DelState(id)
}

// AssetExists returns true when asset with given ID exists in world state
func (s *SmartContract) AssetExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) {
    assetJSON, err := ctx.GetStub().GetState(id)
    if err != nil {
        return false, fmt.Errorf("failed to read from world state: %v", err)
    }

    return assetJSON != nil, nil
}

// TransferAsset updates the owner field of asset with given id in world state.
func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, newOwner string) error {
    asset, err := s.ReadAsset(ctx, id)
    if err != nil {
        return err
    }

    asset.Owner = newOwner
    assetJSON, err := json.Marshal(asset)
    if err != nil {
        return err
    }

    return ctx.GetStub().PutState(id, assetJSON)
}

// GetAllAssets returns all assets found in world state
func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface) ([]*Asset, error) {
    // range query with empty string for startKey and endKey does an
    // open-ended query of all assets in the chaincode namespace.
    resultsIterator, err := ctx.GetStub().GetStateByRange("", "")
    if err != nil {
        return nil, err
    }
    defer resultsIterator.Close()

    var assets []*Asset
    for resultsIterator.HasNext() {
        queryResponse, err := resultsIterator.Next()
        if err != nil {
            return nil, err
        }

        var asset Asset
        err = json.Unmarshal(queryResponse.Value, &asset)
        if err != nil {
            return nil, err
        }
        assets = append(assets, &asset)
    }

    return assets, nil
}

首先这里执行完初始化链码之后:

peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" -C mychannel -n basic --peerAddresses localhost:7051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt" --peerAddresses localhost:9051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt" -c '{"function":"InitLedger","Args":[]}'

这里记住一个模板:

就是peer chiancode invoke ....... -c '{"function":"函数名","Args":[参数]}'

function就是我们链码里的函数名!

2.0的链码是不需要初始化链码的,1.4版本的是需要的,这里2.0的就按照上面的模板就行,注意,参数这里不管链码里传入上面参数,这里都是用字符串格式,比如,int的4也要写成"4"。

注意,如果你想继续用的1.4的链码也可以,将1.4链码原来的包改成这样就可以在2.0环境中继续使用了!

如果你想使用2.0以上版本的链码,需要用的是这个api:

这里注意,2.0的链码,传参数的时候是不能使用args []string的,如果是1.4的链码是可以的。

这里注意,如果想在2.0链码环境中使用如1.4的传入多个参数 args []string的话,可以用这个api:

function, args := ctx.GetStub().GetFunctionAndParameters()

这里注意,这里的function就是Testargs,后面args直接传入4个args参数即可!

根据返回的args,就是之前在1.4环境中的args!

贴上自己写的链码方法demo:

//-c '{"function":"Testargs","Args":["01","fanwen","dayang","man"]}'
//这里注意args后面传入的参数就是args,function是不需要传入的,不然就会报错"err! number is not 4!",这里是和1.4不一样的地方
func (t *mycc) Testargs(ctx contractapi.TransactionContextInterface) string{
	function, args := ctx.GetStub().GetFunctionAndParameters()//这里的function就是获取了上面的函数名就是Testargs
	if len(args) != 4 {
		return "err! number is not 4!"
	}
	stu := Student{
		StuId:   args[0],
		Name:    args[1],
		Address: args[2],
		Sex:     args[3],
	}
	assetJSON, err := json.Marshal(stu)
	if err != nil {
		return err.Error()
	}
	//根据StuId存入账本
	err = ctx.GetStub().PutState(stu.StuId, assetJSON)
	if err != nil{
		return err.Error()
	}
	return "you use the function:" + function + " put in state ok!"

}

执行效果:

如果在args输入了超过4个参数:

 

就会报错,就是我们在链码里自己写的逻辑判断的错误! 

这里注意,fabric在2.0版本以上的链码中,可以返回任何数据!比如string,int,float64,或者是自定义的结构体或者结构体指针都可以!

注意:传参数的时候,不管传什么参数,在cli端,都是传入的"内容",也就是字符串形式传入的!

如果是查询操作的话,不需要连接orderer结点,也不需要背书策略,直接用以下模板就行:

peer chaincode query -C mychannel -n fan12 -c '{"function":"GetAll","Args":["args"]}'

当然,如果用invoke也可以!看个人习惯。

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值