长安链使用Golang编写智能合约教程(三)

本篇主要介绍长安链Go SDK写智能合约的一些常见方法的使用方法或介绍

资料来源:

  1. 官方文档
  2. 官方示例合约库
  3. 官方SDK接口文档

教程一:智能合约编写1

教程二:智能合约编写2

一、获取参数、获取状态、获取历史记录的方法解析

注意!

这些查询链上数据的方法:只能是查询本合约之前上链的数据,别的合约上链的数据就算key相同你也是不能查的,其他查询方法也一样

        // GetArgs get arg from transaction parameters
	// @return: 参数map
	GetArgs() map[string][]byte
(常用,获取参数的方法,之前的示例基本上都用到)

	// GetState get [key, field] from chain
	// @param key: 获取的参数名
	// @param field: 获取的参数名
	// @return1: 获取结果,格式为string
	// @return2: 获取错误信息
	// Deprecated: 无法通过返回值来判断state是否存在,建议使用GetStateWithExists
	GetState(key, field string) (string, error)

(通过空间域名+key 查询值)官方说废弃了建议用GetStateWithExists 


	// GetStateWithExists get [key, field] from chain
	// @param key: 获取的参数名
	// @param field: 获取的参数名
	// @return1: 获取结果,格式为string
	// @return2: 是否存在,bool, 字符串长度为0不代表不存在
	// @return3: 获取错误信息
	GetStateWithExists(key, field string) (string, bool, error)

(通过空间域名+key 查询值) 返回值,是否存在,错误参数

        其中如果不存在返回空和false


	// GetBatchState get [BatchKeys] from chain
	// @param batchKey: 获取的参数名
	// @return1: 获取结果
	// @return2: 获取错误信息
	GetBatchState(batchKeys []*vmPb.BatchKey) ([]*vmPb.BatchKey, error)

没看懂怎么用,像是批量查询,但是BatchKey很多参数


	// GetStateByte get [key, field] from chain
	// @param key: 获取的参数名
	// @param field: 获取的参数名
	// @return1: 获取结果,格式为[]byte, nil表示不存在
	// @return2: 获取错误信息
	GetStateByte(key, field string) ([]byte, error)

(通过空间域名+key 查询值) 返回值[]byte,错误参数


	// GetStateFromKey get [key] from chain
	// @param key: 获取的参数名
	// @return1: 获取结果,格式为string
	// @return2: 获取错误信息
// Deprecated: 无法通过返回值来判断state是否存在,建议使用GetStateFromKeyWithExists
	GetStateFromKey(key string) (string, error)

注意!

       通过key去查值,如果前面你存值是用  field 、key 两个参数存的,那么你用一个key值是取不到任何值的


	// GetStateFromKeyWithExists get [key] from chain
	// @param key: 获取的参数名
	// @return1: 获取结果,格式为string
	// @return2: 是否存在,bool, 字符串长度为0不代表不存在
	// @return3: 获取错误信息
	GetStateFromKeyWithExists(key string) (string, bool, error)

同上


	// GetStateFromKeyByte get [key] from chain
	// @param key: 获取的参数名
	// @return1: 获取结果,格式为[]byte, nil表示不存在
	// @return2: 获取错误信息
	GetStateFromKeyByte(key string) ([]byte, error)

同上


 

二、存数据、删除数据的一些方法

        // PutState put [key, field, value] to chain
	// @param1 key: 参数名
	// @param1 field: 参数名
	// @param2 value: 参数值,类型为string
	// @return1: 上传参数错误信息
	PutState(key, field string, value string) error

存数据方法:  参数依次是 key值、空间域名、要存的数据 (string)

官方的一些示例是:空间域名、 key值、要存的数据  (string)


	// PutStateByte put [key, field, value] to chain
	// @param1 key: 参数名
	// @param1 field: 参数名
	// @param2 value: 参数值,类型为[]byte
	// @return1: 上传参数错误信息
	PutStateByte(key, field string, value []byte) error

存数据方法:  参数依次是 key值、空间域名、要存的数据  []byte

官方的一些示例是:空间域名、 key值、要存的数据  []byte


	// PutStateFromKey put [key, value] to chain
	// @param1 key: 参数名
	// @param2 value: 参数值,类型为string
	// @return1: 上传参数错误信息
	PutStateFromKey(key string, value string) error

存数据方法:  参数依次是 key值、要存的数据 string

注意 ,不带空间域名去存,取值的时候也要不带空间域名


	// PutStateFromKeyByte put [key, value] to chain
	// @param1 key: 参数名
	// @param2 value: 参数值,类型为[]byte
	// @return1: 上传参数错误信息
	PutStateFromKeyByte(key string, value []byte) error

存数据方法:  参数依次是 key值、要存的数据 []byte


	// DelState delete [key, field] to chain
	// @param1 key: 删除的参数名
	// @param1 field: 删除的参数名
	// @return1:删除参数的错误信息
	DelState(key, field string) error

删除数据方法:  参数依次是 key值、空间域名

注意:删除不是真的删除数据,会新增一条交易,交易内容是 key 、field 的字段isdelete变成 true    ,  并且如果区块链上没有 key、field的数据同样也能删除、也会新增一条数据


	// DelStateFromKey delete [key] to chain
	// @param1 key: 删除的参数名
	// @return1:删除参数的错误信息
	DelStateFromKey(key string) error

删除数据的方法通过key

三、获取其他值的一些方法

        // GetCreatorOrgId get tx creator org id
	// @return1: 合约创建者的组织ID
	// @return2: 获取错误信息
	GetCreatorOrgId() (string, error)

获取合约创建者的组织ID


	// GetCreatorRole get tx creator role
	// @return1: 合约创建者的角色
	// @return2: 获取错误信息
	GetCreatorRole() (string, error)

获取合约创建者的角色


	// GetCreatorPk get tx creator pk
	// @return1: 合约创建者的公钥
	// @return2: 获取错误信息
	GetCreatorPk() (string, error)

获取合约创建者的公钥

在IDE可以获取,在使用证书+私钥的链,还没测过


	// GetSenderOrgId get tx sender org id
	// @return1: 交易发起者的组织ID
	// @return2: 获取错误信息
	GetSenderOrgId() (string, error)

获取交易发起者的组织ID

	// GetSenderRole get tx sender role
	// @return1: 交易发起者的角色
	// @return2: 获取错误信息
	GetSenderRole() (string, error)

获取交易发起者的角色


	// GetSenderPk get tx sender pk
	// @return1: 交易发起者的公钥
	// @return2: 获取错误信息
	GetSenderPk() (string, error)

获取 交易发起者的公钥


	// GetBlockHeight get tx block height
	// @return1: 当前块高度
	// @return2: 获取错误信息
	GetBlockHeight() (int, error)

获取当前块高度


	// GetTxId get current tx id
	// @return1: 交易ID
	// @return2: 获取错误信息
	GetTxId() (string, error)

获取 交易ID


	// GetTxInfo get tx info
	// @param txId :合约交易ID
	GetTxInfo(txId string) protogo.Response

获取合约交易ID


	// GetTxTimeStamp get tx timestamp
	// @return1: 交易timestamp
	// @return2: 获取错误信息
	GetTxTimeStamp() (string, error)

获取交易timestamp


	// EmitEvent emit event, you can subscribe to the event using the SDK
	// @param1 topic: 合约事件的主题
	// @param2 data: 合约事件的数据,参数数量不可大于16
	EmitEvent(topic string, data []string)

发布合约事件,发布了的化,主题和参数,会被订阅者收到


	// Log record log to chain server
	// @param message: 事情日志的信息
	//Deprecated
	Log(message string)

日记记录,会写进节点日志


	// Debugf record log to chain server
	// @param format: 日志格式化模板
	// @param a: 模板参数
	Debugf(format string, a ...interface{})

同上


	// Infof record log to chain server
	// @param format: 日志格式化模板
	// @param a: 模板参数
	Infof(format string, a ...interface{})

同上


	// Warnf record log to chain server
	// @param format: 日志格式化模板
	// @param a: 模板参数
	Warnf(format string, a ...interface{})

同上


	// Errorf record log to chain server
	// @param format: 日志格式化模板
	// @param a: 模板参数
	Errorf(format string, a ...interface{})

同上


	// CallContract invoke another contract and get response
	// @param1: 合约名称
	// @param2: 合约方法
	// @param3: 合约合约参数
	// @return1: 合约结果
CallContract(contractName, method string, args map[string][]byte) protogo.Response

合约里面调用别的合约?不知道是干嘛的

官方解释:跨合约调用,用于调用已经安装的其他合约的

四、获取历史数据的一些方法

// NewIterator range of [startKey, limitKey), front closed back open
	// @param1: 范围查询起始key
	// @param2: 范围查询结束key
	// @return1: 根据起始key生成的迭代器
	// @return2: 获取错误信息
	NewIterator(startKey string, limitKey string) (ResultSetKV, error)

不清出这个起始key、结束key是什么意思,如果key是一些字母也有起始和结束吗?

难以理解,懂得朋友辛苦留言指导一下


	// NewIteratorWithField range of [key+"#"+startField, key+"#"+limitField), front closed back open
	// @param1: 分别与param2, param3 构成查询起始和结束的key
	// @param2: [param1 + "#" + param2] 来获取查询起始的key
	// @param3: [param1 + "#" + param3] 来获取查询结束的key
	// @return1: 根据起始位置生成的迭代器
	// @return2: 获取错误信息
NewIteratorWithField(key string, startField string, limitField string) (ResultSetKV, error)

同上


	// NewIteratorPrefixWithKeyField range of [key+"#"+field, key+"#"+field], front closed back closed
	// @param1: [ param1 + "#" +param2 ] 构成前缀范围查询的key
	// @param2: [ param1 + "#" +param2 ] 构成前缀范围查询的key
	// @return1: 根据起始位置生成的迭代器
	// @return2: 获取错误信息
	NewIteratorPrefixWithKeyField(key string, field string) (ResultSetKV, error)

同上


	// NewIteratorPrefixWithKey range of [key, key], front closed back closed
	// @param1: 前缀范围查询起始key
	// @return1: 根据起始位置生成的迭代器
	// @return2: 获取错误信息
	NewIteratorPrefixWithKey(key string) (ResultSetKV, error)

 查询key相同的所有数据,使用方法可以参考教程二

只能是查询本合约之前上链的数据,别的合约上链的数据就算key相同你也是不能查的,其他查询方法也一样


	// NewHistoryKvIterForKey query all historical data of key, field
	// @param1: 查询历史的key
	// @param2: 查询历史的field
	// @return1: 根据key, field 生成的历史迭代器
	// @return2: 获取错误信息
	NewHistoryKvIterForKey(key, field string) (KeyHistoryKvIter, error)

查询 field 、key相同的所有数据,使用方法可以参考教程二

只能是查询本合约之前上链的数据,别的合约上链的数据就算key相同你也是不能查的,其他查询方法也一样


	// GetSenderAddr Get the address of the origin caller address, same with Origin()
	// @return1: origin caller address
	// @return2: 获取错误信息
	// Deprecated
	GetSenderAddr() (string, error)

被弃用了,别学


	// Sender Get the address of the sender address, if the contract is called by another contract, the result will be
	// the caller contract's address.
	// Sender will return system contract address when executing the init or upgrade method (If you need to return the
	// user address, we recommend using Origin method here), because the init and upgrade methods are cross-contract
	// txs (system contract -> common contract).
	// @return1: sender address
	// @return2: 获取错误信息
	Sender() (string, error)

获取发送者的地址。如果合约是由另一个合约调用的,结果将是调用者合约的地址。


	// Origin Get the address of the tx origin caller address
	// @return1: origin caller address
	// @return2: 获取错误信息
	Origin() (string, error)

获取交易原始调用者的地址

 五、示例合约

以下合约是我在写这个教程测试用的,只写了部分接口测试

/*
Copyright (C) BABEC. All rights reserved.
Copyright (C) THL A29 Limited, a Tencent company. All rights reserved.

SPDX-License-Identifier: Apache-2.0
*/

package main

import (
	"chainmaker/pb/protogo"
	"chainmaker/sandbox"
	"chainmaker/sdk"
	"encoding/json"
	"fmt"
	"log"
	"strconv"
)

type FactContract struct {
}

// 存证对象
type Fact struct {
	FileHash string
	FileName string
	Time     int
}

// 新建存证对象
func NewFact(fileHash string, fileName string, time int) *Fact {
	fact := &Fact{
		FileHash: fileHash,
		FileName: fileName,
		Time:     time,
	}
	return fact
}

func (f *FactContract) InitContract() protogo.Response {
	return sdk.Success([]byte("Init contract success"))
}

func (f *FactContract) UpgradeContract() protogo.Response {
	return sdk.Success([]byte("Upgrade contract success"))
}

func (f *FactContract) InvokeContract(method string) protogo.Response {
	switch method {
	case "save":
		return f.Save()
	case "findByFileHash":
		return f.FindByFileHash()
	case "deltedByFileHash":
		return f.DeleteByFileHash()
	case "getHistoryByFileHash":
		return f.GetHistoryByFileHash()
	case "getStateFromKey":
		return f.GetStateFromKey()
	case "getStateByte":
		return f.GetStateByte()
	case "getStateWithExists":
		return f.GetStateWithExists()
	case "getCreatorOrgId":
		return f.GetCreatorOrgId()
	case "getCreatorPk":
		return f.GetCreatorPk()
	case "GetSenderOrgId":
		return f.GetSenderOrgId()
	case "GetSenderRole":
		return f.GetSenderRole()
	case "GetSenderPk":
		return f.GetSenderPk()
	case "GetBlockHeight":
		return f.GetBlockHeight()
	case "GetTxId":
		return f.GetTxId()
	case "GetTxTimeStamp":
		return f.GetTxTimeStamp()
	default:
		return sdk.Error("invalid method")
	}
}

func (f *FactContract) Save() protogo.Response {
	params := sdk.Instance.GetArgs()

	// 获取参数
	fileHash := string(params["file_hash"])
	fileName := string(params["file_name"])
	timeStr := string(params["time"])
	time, err := strconv.Atoi(timeStr)
	if err != nil {
		msg := "time is [" + timeStr + "] not int"
		sdk.Instance.Errorf(msg)
		return sdk.Error(msg)
	}

	// 构建结构体
	fact := NewFact(fileHash, fileName, time)

	// 序列化
	factBytes, err := json.Marshal(fact)
	if err != nil {
		return sdk.Error(fmt.Sprintf("传过来的参数序列化失败, err: %s", err))
	}
	// 发送事件
	sdk.Instance.EmitEvent("topic_vx", []string{fact.FileHash, fact.FileName})

	// 存储数据
	err = sdk.Instance.PutStateByte("fact_bytes", fact.FileHash, factBytes)
	if err != nil {
		return sdk.Error("fail to save fact bytes")
	}

	// 记录日志
	// sdk.Instance.Infof("[save] fileHash=" + fact.FileHash)
	// sdk.Instance.Infof("[save] fileName=" + fact.FileName)
	createUser, _ := sdk.Instance.GetSenderRole()
	sdk.Instance.Infof("[saveUser] create=" + createUser)

	// 返回结果
	return sdk.Success([]byte(fact.FileName + fact.FileHash))

}

func (f *FactContract) FindByFileHash() protogo.Response {
	// 获取参数
	fileHash := string(sdk.Instance.GetArgs()["file_hash"])

	// 查询结果
	result, err := sdk.Instance.GetStateByte("fact_bytes", fileHash)
	if err != nil {
		return sdk.Error("failed to call get_state")
	}

	// 反序列化
	var fact Fact
	if err = json.Unmarshal(result, &fact); err != nil {
		return sdk.Error(fmt.Sprintf("unmarshal fact failed, err: %s", err))
	}

	// 记录日志
	sdk.Instance.Infof("[find_by_file_hash] fileHash=" + fact.FileHash)
	sdk.Instance.Infof("[find_by_file_hash] fileName=" + fact.FileName)

	// 返回结果
	return sdk.Success(result)
}

func (f *FactContract) DeleteByFileHash() protogo.Response {
	// 获取参数
	fileHash := string(sdk.Instance.GetArgs()["file_hash"])

	// 查询结果
	err := sdk.Instance.DelState("fact_bytes", fileHash)
	if err != nil {
		return sdk.Error("failed to delere get_state")
	}

	// 返回结果
	return sdk.Success(nil)
}

func (f *FactContract) GetHistoryByFileHash() protogo.Response {
	// 获取参数
	fileHash := string(sdk.Instance.GetArgs()["file_hash"])

	// 查询结果
	iter, err := sdk.Instance.NewHistoryKvIterForKey("fact_bytes", fileHash)
	if err != nil {
		return sdk.Error("failed to delere get_state")
	}
	defer iter.Close()
	var keyModifications []*sdk.KeyModification
	// 遍历结果
	for {
		if !iter.HasNext() {
			break
		}
		keyModification, err := iter.Next()
		if err != nil {
			sdk.Instance.Infof("Error iterating: %v", err)
		}
		if keyModification == nil {
			break
		}

		keyModifications = append(keyModifications, keyModification)

		sdk.Instance.Infof("Key: %s, Field: %s, Value: %s, TxId: %s, BlockHeight: %d, IsDelete: %t, Timestamp: %s, \n",
			keyModification.Key, keyModification.Field, keyModification.Value, keyModification.TxId, keyModification.BlockHeight, keyModification.IsDelete, keyModification.Timestamp)
	}

	jsonBytes, err := json.Marshal(keyModifications)
	if err != nil {
		return sdk.Error(fmt.Sprintf("Error marshaling keyModifications: %v", err))
	}

	// 返回结果
	return sdk.Success(jsonBytes)
}

func (f *FactContract) GetStateFromKey() protogo.Response {
	// 获取参数
	fileHash := string(sdk.Instance.GetArgs()["file_hash"])

	// 查询结果
	result, err := sdk.Instance.GetStateFromKey(fileHash)
	if err != nil {
		return sdk.Error("failed to call get_state")
	}

	sdk.Instance.Infof("[GetStateFromKey] result=" + result)

	byteSlice := []byte(result)

	// 返回结果
	return sdk.Success(byteSlice)
}

func (f *FactContract) GetStateByte() protogo.Response {
	// 获取参数
	fileHash := string(sdk.Instance.GetArgs()["file_hash"])

	// 查询结果
	result, err := sdk.Instance.GetStateByte("fact_bytes", fileHash)
	if err != nil {
		return sdk.Error("failed to call get_state")
	}

	sdk.Instance.Infof("[GetStateByte] result=" + string(result))

	// 返回结果
	return sdk.Success(result)
}

func (f *FactContract) GetStateWithExists() protogo.Response {
	// 获取参数
	fileHash := string(sdk.Instance.GetArgs()["file_hash"])

	// 查询结果
	result, exit, err := sdk.Instance.GetStateWithExists("fact_bytes", fileHash)
	if err != nil {
		return sdk.Error("failed to call get_state")
	}

	exitStr := fmt.Sprintf("%v", exit)
	sdk.Instance.Infof("[GetStateByte] result=" + result + "bool:" + string(exitStr))

	// 返回结果
	return sdk.Success([]byte(result))
}

func (f *FactContract) GetCreatorOrgId() protogo.Response {

	// 查询结果
	result, err := sdk.Instance.GetCreatorOrgId()
	if err != nil {
		return sdk.Error("failed to call get_state")
	}

	sdk.Instance.Infof("[GetCreatorOrgId] GetCreatorOrgId=" + result)

	// 返回结果
	return sdk.Success([]byte(result))
}

func (f *FactContract) GetCreatorPk() protogo.Response {

	// 查询结果
	result, err := sdk.Instance.GetCreatorPk()
	if err != nil {
		return sdk.Error("failed to call get_state")
	}

	sdk.Instance.Infof("[GetCreatorPk] GetCreatorPk=" + result)

	// 返回结果
	return sdk.Success([]byte(result))
}

func (f *FactContract) GetSenderOrgId() protogo.Response {

	// 查询结果
	result, err := sdk.Instance.GetSenderOrgId()
	if err != nil {
		return sdk.Error("failed to call get_state")
	}

	sdk.Instance.Infof("[GetSenderOrgId] GetSenderOrgId=" + result)

	// 返回结果
	return sdk.Success([]byte(result))
}

func (f *FactContract) GetSenderRole() protogo.Response {

	// 查询结果
	result, err := sdk.Instance.GetSenderRole()
	if err != nil {
		return sdk.Error("failed to call get_state")
	}

	sdk.Instance.Infof("[GetSenderRole] GetSenderRole=" + result)

	// 返回结果
	return sdk.Success([]byte(result))
}

func (f *FactContract) GetSenderPk() protogo.Response {

	// 查询结果
	result, err := sdk.Instance.GetSenderPk()
	if err != nil {
		return sdk.Error("failed to call get_state")
	}

	sdk.Instance.Infof("[GetSenderPk] GetSenderPk=" + result)

	// 返回结果
	return sdk.Success([]byte(result))
}
func (f *FactContract) GetBlockHeight() protogo.Response {

	// 查询结果
	result, err := sdk.Instance.GetBlockHeight()
	if err != nil {
		return sdk.Error("failed to call get_state")
	}

	str := strconv.Itoa(result) // 将int转换为字符串
	bytes := []byte(str)        // 将字符串转换为[]byte
	// 返回结果
	return sdk.Success(bytes)
}

func (f *FactContract) GetTxId() protogo.Response {

	// 查询结果
	result, err := sdk.Instance.GetTxId()
	if err != nil {
		return sdk.Error("failed to call get_state")
	}

	sdk.Instance.Infof("[GetTxId] GetTxId=" + result)

	// 返回结果
	return sdk.Success([]byte(result))
}

func (f *FactContract) GetTxTimeStamp() protogo.Response {

	// 查询结果
	result, err := sdk.Instance.GetTxTimeStamp()
	if err != nil {
		return sdk.Error("failed to call get_state")
	}

	sdk.Instance.Infof("[GetTxTimeStamp] GetTxTimeStamp=" + result)

	// 返回结果
	return sdk.Success([]byte(result))
}

func main() {
	err := sandbox.Start(new(FactContract))
	if err != nil {
		log.Fatal(err)
	}
}

  • 25
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用Golang编写一个简单的HTTP服务器可以有多种方式。下面是两种常见的实现方式: 第一种方式是使用`http.HandleFunc`函数和一个处理函数来实现。代码如下所示: ```go package main import ( "fmt" "net/http" ) func indexHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "hello world") } func main() { http.HandleFunc("/", indexHandler) http.ListenAndServe(":8000", nil) } ``` 在这个例子中,我们定义了一个`indexHandler`函数来处理根路径的请求。当有请求到达时,`indexHandler`函数会将"hello world"作为响应写入到`http.ResponseWriter`中。 第二种方式是使用自定义的处理器类型和`http.Handle`函数来实现。代码如下所示: ```go package main import ( "fmt" "net/http" ) type indexHandler struct { content string } func (ih *indexHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, ih.content) } func main() { http.Handle("/", &indexHandler{content: "hello world!"}) http.ListenAndServe(":8001", nil) } ``` 在这个例子中,我们定义了一个`indexHandler`类型,并实现了`ServeHTTP`方法。当有请求到达时,`ServeHTTP`方法会将`indexHandler`的`content`字段作为响应写入到`http.ResponseWriter`中。 无论是哪种方式,我们都需要使用`http.ListenAndServe`函数来启动服务器。这个函数会监听指定的地址,并将请求交给指定的处理器来处理。 希望这个例子能帮助你理解如何使用Golang编写一个简单的HTTP服务器。 #### 引用[.reference_title] - *1* *2* *3* [golang http Server介绍](https://blog.csdn.net/zrg3699/article/details/122280399)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值