go语言实现NFT

2023-10-30

此文章以及链码/合约,均为原创,已通过以heco-nft记录为NFT资产(HecoInfo Transaction Hash (Txhash) Details | HecoInfo),请勿转载,如有兴趣,可扫描文章最后的二维码。

基于erc721的以太坊NFT的流转基本上是这样的:

    发布NFT合约A  ---->    基于合约A,存储NFT内容,生成基于合约A的 token   -->调用转赠函数可以转赠token给某个钱包地址  

如何通过Fabric go语言实现类似以太坊NFT的功能?

以下是基础。

   1 需要对以太坊NFT合约,ERC721协议进行深入研究,了解协议原理和协议内容

   2 通过基于以太坊/heco-火币链等发布ERC721协议成功过,并且执行成功过,熟悉流程

   3 基于ERC721协议的内容,通过go语言的链码 实现基于Fabric的NFT

说明:

    签名部分,做了校验,如需改动,把签名部分去掉即可。

功能:

该链码,实现了ERC721协议的全部内容功能包括:

   1 生成NFT-token 

   2转赠NFT-token

   3 授权NFT-token给其他人

   4 提现NFT-token(即取消授权给其他人)

   5 查看NFT基本信息

   6 发布多种NFT资产

   7 转赠NFT资产的控制人

   8 查看资产余额

   9 资产信息

   10 资产转移记录列表

   ...等

存在的问题:

1 fabric的mvcc机制,不支持针对同一个key,在同一个块中的关联交易

•交易的模拟执行mvcc阶段,生成读写集,同一块中多笔涉及同一个key的修改操作,只有第一笔会别认为成功。

•假设:A余额100  A-B 转账 1
•                             A-C 转账 1
•          两笔交易发生在一个区块中,则只有第一笔会成功。

2 fabric的councdb存在诸多问题,

  •CouchDB一旦失败,就要终止所有正在运行的查询,甚至包括数据复制与压缩

 •CouchDB常常会发生视图丢失索引的情况,除非删除视图文件并重启数据库,否则很难做到重新索引

代码如下:

package main
 
import (
	"bccsp"
	"crypto"
	"crypto/rsa"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/hyperledger/fabric-chaincode-go/shim"
	"github.com/hyperledger/fabric-protos-go/peer"
	"math/big"
	"sort"
	"strconv"
	"strings"
	"time"
)
 
// NFT资产
type address string
type tokenId string
type url string
type transTime int
 
type Asset struct {
	NftName          string                            `json:"nftName"`               //资产token名称
	NftSymbol        string                            `json:"nftSymbol"`             //资产token简称
	Owner            string                            `json:"owner"`                 //资产token创建人
	AssetOf          map[address]map[tokenId]url       `json:"assetOf"`               //资产token的持有人和持有资产的集合
	AssetHistory     map[tokenId]map[transTime]address `json:"assetHistory"`          //资产token转移记录
	AssetApprove     map[tokenId]address               `json:"assetApprove"`          //资产授权人
	AssetExit        map[url]bool                      `json:"assetExit"`             //资产token是否已经存在
	AssetTokenIdExit map[tokenId]bool                  `json:"assetTokenIdExit"`      //资产token是否已经存在
	TotalSupply      int                               `json:"totalSupply,omitempty"` //资产总量 0代表不限量NFT 非0  则代表限量版本的NFT
}
 
// 交易结构
type TransferAsset struct {
	_From      address `json:"_from,omitempty"`      // 转出人 / 资产owner
	_To        address `json:"_to"`                  // 确权/转移到的地址
	_TokenId   tokenId `json:"_tokenId"`             // 每一个存证确权为一个资产token 每个资产token有唯一的tokenId
	_Url       url     `json:"_url"`                 // 存证的txid  或者 资产的url
	_Data      string  `json:"_data,omitempty"`      // 备注
	_Signature string  `json:"_signature,omitempty"` // 签名
	_SignType  string  `json:"_signType,omitempty"`  // 签名类型
	_NftSymbol string  `json:"_nftSymbol"`           //资产token简称
}
 
func (u *Asset) Init(stub shim.ChaincodeStubInterface) peer.Response {
	return shim.Success(nil)
}
 
// 初始化NFT资产合约 名称 简称 创造人
func (s *Asset) initLedger(stub shim.ChaincodeStubInterface, args []string) peer.Response {
 
	if len(args) != 4 {
		return shim.Error("Incorrect number of arguments. Expecting 2")
	}
 
	name := args[0]                         // nft资产名称
	symbol := args[1]                       // nft资产简称
	owner := args[2]                        // nft资产创始人  pubkey
	totalSupply, _ := strconv.Atoi(args[3]) // nft资产是否为限量资产
 
	// 检查当前token是否已经被创建
	value, err := stub.GetState(symbol)
	if err != nil {
		return shim.Error(err.Error())
	}
	if value != nil || len(value) > 0 {
		return shim.Error("symbol 已存在")
	}
 
	assetHistory := make(map[tokenId]map[transTime]address)
	assetApprove := make(map[tokenId]address)
	assetOf := make(map[address]map[tokenId]url)
	assetExit := make(map[url]bool)
	assetTokenIdExit := make(map[tokenId]bool)
 
	// 创建新的token
	asset := &Asset{
		NftName:          name,
		NftSymbol:        symbol,
		Owner:            owner,
		AssetOf:          assetOf,
		AssetHistory:     assetHistory,
		AssetApprove:     assetApprove,
		AssetExit:        assetExit,
		AssetTokenIdExit: assetTokenIdExit,
		TotalSupply:      totalSupply,
	}
 
	//存储token
	tokenAsBytes, _ := json.Marshal(asset)
	err = stub.PutState(symbol, tokenAsBytes)
	if err != nil {
		return shim.Error(err.Error())
	}
	fmt.Printf("Init %s \n", string(tokenAsBytes))
	return shim.Success(nil)
}
 
// invoke 执行
func (u *Asset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
	funcName, args := stub.GetFunctionAndParameters()
	switch funcName {
	case "mine":
		return u.mine(stub, args)
	case "initLedger":
		return u.initLedger(stub, args)
	case "transFrom":
		return u.transFrom(stub, args)
	case "approve":
		return u.approve(stub, args)
	case "withDrawal":
		return u.withDrawal(stub, args)
	case "balanceof":
		return u.balanceof(stub, args)
	case "getApproved":
		return u.getApproved(stub, args)
	case "ownerof":
		return u.ownerof(stub, args)
	case "tokenURL":
		return u.tokenURL(stub, args)
	case "name":
		return u.name(stub, args)
	case "owner":
		return u.owner(stub, args)
	case "symbol":
		return u.symbol(stub, args)
	case "totalSupply":
		return u.totalSupply(stub, args)
	case "tokenBasic":
		return u.tokenBasic(stub, args)
	default:
		return shim.Error("no such function")
	}
}
 
// 确权资产
func (u *Asset) mine(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args) != 6 {
		return shim.Error("expect six args")
	}
 
	// 生成资产的唯一id  uuid 由外部传入 因为fabric机制原因 内务无法统一uuid
	transferAsset := TransferAsset{
		_NftSymbol: args[0],
		_From:      address(args[1]),
		_To:        address(args[1]),
		_TokenId:   tokenId(args[2]),
		_Url:       url(args[3]),
		_Signature: args[4],
		_SignType:  args[5],
	}
	fmt.Printf("mine tokenname:%s \n _to:%s \n _tokenId:%s \n url:%s...\n", transferAsset._NftSymbol, transferAsset._To, transferAsset._TokenId, transferAsset._Url)
 
	//验证  当前的 nft token symbol简称存不存在
	assetValue, errAsset := stub.GetState(transferAsset._NftSymbol)
	if errAsset != nil {
		return shim.Error(errAsset.Error())
	}
	if assetValue == nil || len(assetValue) == 0 {
		return shim.Error("_symbol 不存在")
	}
 
	//只有存在,也就是初始化过了的nft token 进行下一步   验签
	veriftResul, errVerify := u.onlyOwner(transferAsset)
	if errVerify != nil || veriftResul == false {
		return shim.Error("Signature verification failed")
	}
 
	nftToken := Asset{}
	json.Unmarshal(assetValue, &nftToken)
 
	if nftToken.AssetTokenIdExit[transferAsset._TokenId] {
		return shim.Error("tokenId already exit")
	}
 
	// 资产的添加
	errMine := nftToken.mineOnly(stub, &transferAsset)
	if errMine != nil {
		return shim.Error(errMine.Error())
	}
 
	// 存储
	assetBytes, err := json.Marshal(&nftToken)
	if err != nil {
		return shim.Error(err.Error())
	}
	err = stub.PutState(args[0], assetBytes)
	if err != nil {
		return shim.Error(err.Error())
	}
	fmt.Println("putState success...")
	return shim.Success(nil)
}
 
//确权时更改资产持有属性
func (u *Asset) mineOnly(stub shim.ChaincodeStubInterface, trans *TransferAsset) error {
 
	//判断是否已经存在该url
	_, ok := u.AssetExit[trans._Url]
	if !ok {
		//添加资产持有记录
		if u.AssetOf[trans._To] == nil {
			mc := make(map[tokenId]url)
			mc[trans._TokenId] = trans._Url
			u.AssetOf[trans._To] = mc
		} else {
			u.AssetOf[trans._To][trans._TokenId] = trans._Url
		}
 
		//添加资产已经存在的标志
		u.AssetExit[trans._Url] = true
 
		if u.AssetHistory[trans._TokenId] == nil {
			//添加资产转移记录
			mh := make(map[transTime]address)
			t1, _ := stub.GetTxTimestamp()
			time1 := time.Unix(t1.Seconds, int64(t1.Nanos))
			timeMi:=time1.UnixNano()/1e6
 
			mh[transTime(timeMi)] = trans._To
			u.AssetHistory[trans._TokenId] = mh
		} else {
			t1, _ := stub.GetTxTimestamp()
			time1 := time.Unix(t1.Seconds, int64(t1.Nanos))
			timeMi:=time1.UnixNano()/1e6
			u.AssetHistory[trans._TokenId][transTime(timeMi)] = trans._To
		}
 
		u.AssetTokenIdExit[trans._TokenId] = true
 
		//判断资产总量是否是否到达限制
		if len(u.AssetExit) > u.TotalSupply {
			fmt.Println("当前nft资产已达到限制")
			return errors.New("TotalSupply already run out")
		}
		return nil
	} else {
		return errors.New("url already exist")
	}
 
}
 
//交易时更改资产持有属性
func (u *Asset) transOnly(stub shim.ChaincodeStubInterface, trans *TransferAsset) error {
 
	//修改资产持有记录 1 删除当前from的资产 2 增加to的资产
	delete(u.AssetOf[(trans._From)], trans._TokenId)
 
	if u.AssetOf[(trans._To)] == nil {
		mc := make(map[tokenId]url)
		mc[trans._TokenId] = trans._Url
		u.AssetOf[(trans._To)] = mc
	} else {
		u.AssetOf[(trans._To)][trans._TokenId] = trans._Url
	}
 
	//添加资产转移记录
	t1, _ := stub.GetTxTimestamp()
	time1 := time.Unix(t1.Seconds, int64(t1.Nanos))
	timeMi:=time1.UnixNano()/1e6
	u.AssetHistory[trans._TokenId][transTime(timeMi)] = trans._To
	return nil
 
}
 
//资产转移
func (u *Asset) transFrom(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args) != 6 {
		return shim.Error("expect six args")
	}
 
	transferAsset := TransferAsset{
		_NftSymbol: args[0],
		_From:      address(args[1]),
		_To:        address(args[2]),
		_TokenId:   tokenId(args[3]),
		_Signature: args[4],
		_SignType:  args[5],
	}
	fmt.Printf("transFrom tokenname:%s \n _to:%s \n _tokenId:%s \n url:%s...\n", transferAsset._NftSymbol, transferAsset._To, transferAsset._TokenId, transferAsset._Url)
 
	//验证  当前的 nft token symbol简称存不存在
	assetValue, errAsset := stub.GetState(transferAsset._NftSymbol)
	if errAsset != nil {
		return shim.Error(errAsset.Error())
	}
	if assetValue == nil || len(assetValue) == 0 {
		return shim.Error("_symbol not exist")
	}
	nftToken := Asset{}
	json.Unmarshal(assetValue, &nftToken)
 
	if !nftToken.AssetTokenIdExit[transferAsset._TokenId] {
		return shim.Error("_tokenId not exist")
	}
 
	// 判断是否有权限进行交易 是否是token持有人 是否是token授权人
	beProved :=false
	if nftToken.AssetOf[transferAsset._From] != nil && nftToken.AssetOf[transferAsset._From][transferAsset._TokenId] == "" {
		if(nftToken.AssetApprove[transferAsset._TokenId]==transferAsset._From){
			fmt.Println("tokenId will be approved trans")
			beProved = true
		}else {
			return shim.Error("your have No permission")
		}
	}
 
	// 判断授权后的交易是否交易给自己
	var keys []int
	for k := range nftToken.AssetHistory[tokenId(transferAsset._TokenId)] {
		keys = append(keys, int(k))
	}
	sort.Ints(keys)
	_keyInt := keys[len(keys)-1]
	_Owner := nftToken.AssetHistory[tokenId(transferAsset._TokenId)][transTime(_keyInt)]
	if transferAsset._To ==_Owner {
		return shim.Error("You are not allowed to sell it to yourself")
	}
 
	transferAsset._Url = nftToken.AssetOf[_Owner][transferAsset._TokenId]
	//只有存在,也就是初始化过了的nft token 进行下一步   验签
	veriftResul, errVerify := nftToken.onlyOwner(transferAsset)
	if errVerify != nil || veriftResul == false {
		return shim.Error("Signature verification failed")
	}
 
	// 资产的转移
	errMine := nftToken.transOnly(stub, &transferAsset)
	if errMine != nil {
		return shim.Error(errMine.Error())
	}
	// 如果是授权账户发起的 则取消授权
	if beProved{
		delete(nftToken.AssetApprove,transferAsset._TokenId)
	}
 
	// 存储
	assetBytes, err := json.Marshal(&nftToken)
	if err != nil {
		return shim.Error(err.Error())
	}
	err = stub.PutState(args[0], assetBytes)
	if err != nil {
		return shim.Error(err.Error())
	}
	fmt.Println("putState success...")
	return shim.Success(nil)
}
 
//资产授权--授权后,资产权限归平台所有,资产所有人无法转移
func (u *Asset) approve(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args) != 5 {
		return shim.Error("expect five args")
	}
 
	_nftSymbol := args[0]
	_approved := args[1]
	_tokenId := args[2]
	_signature := args[3]
	_signType := args[4]
 
	// todo  缺少_approved地址的合法性校验
 
	fmt.Printf("approve _nftSymbol:%s \n _approved:%s \n  _tokenId:%s \n", _nftSymbol, _approved, _tokenId)
 
	//验证  当前的 nft token symbol简称存不存在
	assetValue, errAsset := stub.GetState(_nftSymbol)
	if errAsset != nil {
		return shim.Error(errAsset.Error())
	}
	if assetValue == nil || len(assetValue) == 0 {
		return shim.Error("_symbol 不存在")
	}
 
	// nfttoken
	nftToken := Asset{}
	json.Unmarshal(assetValue, &nftToken)
 
	var keys []int
	for k := range nftToken.AssetHistory[tokenId(_tokenId)] {
		keys = append(keys, int(k))
	}
	sort.Ints(keys)
	_keyInt := keys[len(keys)-1]
	_Owner := nftToken.AssetHistory[tokenId(_tokenId)][transTime(_keyInt)]
	if _Owner == address(_approved) {
		return shim.Error("You can't delegate to yourself")
	}
 
	_url := nftToken.AssetOf[_Owner][tokenId(_tokenId)]
 
	transferAsset := TransferAsset{
		_Url:       _url,
		_From:      _Owner,
		_Signature: _signature,
		_SignType:  _signType,
	}
 
	//  验签
	veriftResul, errVerify := nftToken.onlyOwner(transferAsset)
	if errVerify != nil || veriftResul == false {
		return shim.Error("Signature verification failed")
	}
 
	// 资产的授权
	if len(nftToken.AssetApprove[tokenId(_tokenId)]) != 0 {
		return shim.Error("Authorization cannot be repeated")
	} else {
		nftToken.AssetApprove[tokenId(_tokenId)] = address(_approved)
	}
 
	// 存储
	assetBytes, err := json.Marshal(&nftToken)
	if err != nil {
		return shim.Error(err.Error())
	}
	err = stub.PutState(args[0], assetBytes)
	if err != nil {
		return shim.Error(err.Error())
	}
	fmt.Println("putState success...")
	return shim.Success(nil)
}
 
//资产提现--授权的资产 取消授权  即为提现,提现后的资产可以交易转移,授权后未提现的,无法转移
func (u *Asset) withDrawal(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args) != 4 {
		return shim.Error("expect four args")
	}
 
	_nftSymbol := args[0]
	_tokenId := args[1]
	_signature := args[2]
	_signType := args[3]
 
	fmt.Printf("withDrawal _nftSymbol:%s \n  _tokenId:%s \n", _nftSymbol, _tokenId)
 
	//验证  当前的 nft token symbol简称存不存在
	assetValue, errAsset := stub.GetState(_nftSymbol)
	if errAsset != nil {
		return shim.Error(errAsset.Error())
	}
	if assetValue == nil || len(assetValue) == 0 {
		return shim.Error("_symbol not exist ")
	}
 
	//nfttoken
	nftToken := Asset{}
	json.Unmarshal(assetValue, &nftToken)
 
	//判断是否已经授权过
	if len(nftToken.AssetApprove[tokenId(_tokenId)]) == 0 {
		return shim.Error("have not approve,no promission do")
	}
 
	var keys []int
	for k := range nftToken.AssetHistory[tokenId(_tokenId)] {
		keys = append(keys, int(k))
	}
	sort.Ints(keys)
	_keyInt := keys[len(keys)-1]
	_Owner := nftToken.AssetHistory[tokenId(_tokenId)][transTime(_keyInt)]
 
	_url := nftToken.AssetOf[_Owner][tokenId(_tokenId)]
 
	transferAsset := TransferAsset{
		_Url:       _url,
		_From:      _Owner,
		_Signature: _signature,
		_SignType:  _signType,
	}
 
	//  验签
	veriftResul, errVerify := nftToken.onlyOwner(transferAsset)
	if errVerify != nil || veriftResul == false {
		return shim.Error("Signature verification failed")
	}
 
	// 提现
	delete(nftToken.AssetApprove, tokenId(_tokenId))
 
	// 存储
	assetBytes, err := json.Marshal(&nftToken)
	if err != nil {
		return shim.Error(err.Error())
	}
	err = stub.PutState(args[0], assetBytes)
	if err != nil {
		return shim.Error(err.Error())
	}
	fmt.Println("putState success...")
	return shim.Success(nil)
}
 
//查询资产余额
func (u *Asset) balanceof(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args) != 2 {
		return shim.Error("expect two args")
	}
 
	_nftSymbol := args[0]
	_owner := args[1]
	fmt.Printf("balanceof _nftSymbol:%s \n  _owner:%s \n", _nftSymbol, _owner)
 
	//验证  当前的 nft token symbol简称存不存在
	assetValue, errAsset := stub.GetState(_nftSymbol)
	if errAsset != nil {
		return shim.Error(errAsset.Error())
	}
	if assetValue == nil || len(assetValue) == 0 {
		return shim.Error("_symbol 不存在")
	}
 
	//nfttoken
	nftToken := Asset{}
	json.Unmarshal(assetValue, &nftToken)
 
	_ownerMap := nftToken.AssetOf[address(_owner)]
 
	_ownerMapAsBytes, _ := json.Marshal(_ownerMap)
 
	fmt.Println("putState success...")
	return shim.Success(_ownerMapAsBytes)
}
 
//查询资产是否已经被授权  返回 true /false
func (u *Asset) getApproved(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args) != 2 {
		return shim.Error("expect two args")
	}
 
	_nftSymbol := args[0]
	_tokenId := args[1]
	fmt.Printf("getApproved _nftSymbol:%s \n  _tokenId:%s \n", _nftSymbol, _tokenId)
 
	//验证  当前的 nft token symbol简称存不存在
	assetValue, errAsset := stub.GetState(_nftSymbol)
	if errAsset != nil {
		return shim.Error(errAsset.Error())
	}
	if assetValue == nil || len(assetValue) == 0 {
		return shim.Error("_symbol 不存在")
	}
 
	//nfttoken
	nftToken := Asset{}
	json.Unmarshal(assetValue, &nftToken)
 
	if len(nftToken.AssetApprove[tokenId(_tokenId)]) == 0 {
		res, _ := json.Marshal(false)
		return shim.Success(res)
	}
	res, _ := json.Marshal(true)
	return shim.Success(res)
}
 
//资产tokenId 获取实际持有人
func (u *Asset) ownerof(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args) != 2 {
		return shim.Error("expect two args")
	}
 
	_nftSymbol := args[0]
	_tokenId := args[1]
 
	fmt.Printf("ownerof _nftSymbol:%s \n  _tokenId:%s \n", _nftSymbol, _tokenId)
 
	//验证  当前的 nft token symbol简称存不存在
	assetValue, errAsset := stub.GetState(_nftSymbol)
	if errAsset != nil {
		return shim.Error(errAsset.Error())
	}
	if assetValue == nil || len(assetValue) == 0 {
		return shim.Error("_symbol 不存在")
	}
 
	//nfttoken
	nftToken := Asset{}
	json.Unmarshal(assetValue, &nftToken)
 
	// 判断tokenId存不存在
	if len(nftToken.AssetHistory[tokenId(_tokenId)]) == 0 {
		return shim.Error("tokenId 不存在")
	}
 
	//查找实际控制人
	var keys []int
	for k := range nftToken.AssetHistory[tokenId(_tokenId)] {
		keys = append(keys, int(k))
	}
	sort.Ints(keys)
	_keyInt := keys[len(keys)-1]
	_Owner := nftToken.AssetHistory[tokenId(_tokenId)][transTime(_keyInt)]
 
	ownerBytes, _ := json.Marshal(_Owner)
 
	return shim.Success(ownerBytes)
}
 
//资产tokenId 获取token的url指向 源文件所在位置
func (u *Asset) tokenURL(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args) != 2 {
		return shim.Error("expect two args")
	}
 
	_nftSymbol := args[0]
	_tokenId := args[1]
 
	fmt.Printf("ownerof _nftSymbol:%s \n  _tokenId:%s \n", _nftSymbol, _tokenId)
 
	//验证  当前的 nft token symbol简称存不存在
	assetValue, errAsset := stub.GetState(_nftSymbol)
	if errAsset != nil {
		return shim.Error(errAsset.Error())
	}
	if assetValue == nil || len(assetValue) == 0 {
		return shim.Error("_symbol 不存在")
	}
 
	//nfttoken
	nftToken := Asset{}
	json.Unmarshal(assetValue, &nftToken)
 
	// 判断tokenId存不存在
	if len(nftToken.AssetHistory[tokenId(_tokenId)]) == 0 {
		return shim.Error("tokenId 不存在")
	}
 
	//查找实际控制人
	var keys []int
	for k := range nftToken.AssetHistory[tokenId(_tokenId)] {
		keys = append(keys, int(k))
	}
	sort.Ints(keys)
	_keyInt := keys[len(keys)-1]
	_Owner := nftToken.AssetHistory[tokenId(_tokenId)][transTime(_keyInt)]
 
	// 获取url
	urlFinal := nftToken.AssetOf[_Owner][tokenId(_tokenId)]
 
	urlBytes, _ := json.Marshal(urlFinal)
	return shim.Success(urlBytes)
}
 
//资产 获取token name
func (u *Asset) name(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args) != 1 {
		return shim.Error("expect one args")
	}
 
	_nftSymbol := args[0]
 
	fmt.Printf("name _nftSymbol:%s \n", _nftSymbol)
 
	//验证  当前的 nft token symbol简称存不存在
	assetValue, errAsset := stub.GetState(_nftSymbol)
	if errAsset != nil {
		return shim.Error(errAsset.Error())
	}
	if assetValue == nil || len(assetValue) == 0 {
		return shim.Error("_symbol 不存在")
	}
 
	//nfttoken
	nftToken := Asset{}
	json.Unmarshal(assetValue, &nftToken)
 
	nameBytes, _ := json.Marshal(nftToken.NftName)
	return shim.Success(nameBytes)
}
 
//资产 获取token owner
func (u *Asset) owner(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args) != 1 {
		return shim.Error("expect one args")
	}
 
	_nftSymbol := args[0]
 
	fmt.Printf("owner _nftSymbol:%s \n", _nftSymbol)
 
	//验证  当前的 nft token symbol简称存不存在
	assetValue, errAsset := stub.GetState(_nftSymbol)
	if errAsset != nil {
		return shim.Error(errAsset.Error())
	}
	if assetValue == nil || len(assetValue) == 0 {
		return shim.Error("_symbol 不存在")
	}
 
	//nfttoken
	nftToken := Asset{}
	json.Unmarshal(assetValue, &nftToken)
 
	ownerBytes, _ := json.Marshal(nftToken.Owner)
	return shim.Success(ownerBytes)
}
 
//资产 获取token symbol
func (u *Asset) symbol(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args) != 1 {
		return shim.Error("expect one args")
	}
 
	_nftSymbol := args[0]
 
	fmt.Printf("symbol _nftSymbol:%s \n", _nftSymbol)
 
	//验证  当前的 nft token symbol简称存不存在
	assetValue, errAsset := stub.GetState(_nftSymbol)
	if errAsset != nil {
		return shim.Error(errAsset.Error())
	}
	if assetValue == nil || len(assetValue) == 0 {
		return shim.Error("_symbol 不存在")
	}
 
	//nfttoken
	nftToken := Asset{}
	json.Unmarshal(assetValue, &nftToken)
 
	nameBytes, _ := json.Marshal(nftToken.NftSymbol)
	return shim.Success(nameBytes)
}
 
//资产 获取token totalSupply
func (u *Asset) totalSupply(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args) != 1 {
		return shim.Error("expect one args")
	}
 
	_nftSymbol := args[0]
 
	fmt.Printf("totalSupply _nftSymbol:%s \n", _nftSymbol)
 
	//验证  当前的 nft token symbol简称存不存在
	assetValue, errAsset := stub.GetState(_nftSymbol)
	if errAsset != nil {
		return shim.Error(errAsset.Error())
	}
	if assetValue == nil || len(assetValue) == 0 {
		return shim.Error("_symbol 不存在")
	}
 
	//nfttoken
	nftToken := Asset{}
	json.Unmarshal(assetValue, &nftToken)
 
	TotalSupplyBytes, _ := json.Marshal(nftToken.TotalSupply)
	return shim.Success(TotalSupplyBytes)
}
 
//资产 获取token 所有基本信息
func (u *Asset) tokenBasic(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args) != 1 {
		return shim.Error("expect one args")
	}
 
	_nftSymbol := args[0]
 
	fmt.Printf("totalSupply _nftSymbol:%s \n", _nftSymbol)
 
	//验证  当前的 nft token symbol简称存不存在
	assetValue, errAsset := stub.GetState(_nftSymbol)
	if errAsset != nil {
		return shim.Error(errAsset.Error())
	}
	if assetValue == nil || len(assetValue) == 0 {
		return shim.Error("_symbol 不存在")
	}
	return shim.Success(assetValue)
}
 
// 获取公钥
/*func getRSAPubkey(codeStr string) (*rsa.PublicKey, error) {
	n := &big.Int{}
	_, flag := n.SetString(codeStr, 16)
	if flag != true {
		return nil, errors.New(fmt.Sprintf("publicKey.n=%s err", codeStr))
	}
	// 构造返回PublicKey引用
	publicKeyObj := &rsa.PublicKey{}
	publicKeyObj.E = 65537
	publicKeyObj.N = n
	return publicKeyObj, nil
}*/
 
// 获取公钥
func getRSAPubkey(codeStr string) (pubKey bccsp.Key, err error) {
	pubArr := strings.Split(codeStr, "|")
	if pubArr == nil || len(pubArr) != 2 {
		return nil, errors.New("pubkey array length invalid")
	}
	e_str := pubArr[0]
	n_str := pubArr[1]
 
	//转换e
	e, err := strconv.Atoi(e_str)
	if err != nil {
		return nil, errors.New("strconv e error")
	}
	//转换n
	n := &big.Int{}
	_, flag := n.SetString(n_str, 16)
	if flag != true {
		return nil, errors.New("strconv n error")
	}
	//构造key
	publickObj := &rsa.PublicKey{}
	publickObj.E = e
	publickObj.N = n
	return &bccsp.RSAPublicKey{publickObj}, nil
}
 
//验证owner所有人 验签
func (u *Asset) onlyOwner(transferAsset TransferAsset) (bool, error) {
	veriftAccount := ""
 
	//是否授权 授权优先验证授权账户 未授权 验证from存不存在,不存在,代表是mine确权资产,使用to验签,存在,代表是资产转移
	if len(u.AssetApprove[transferAsset._TokenId]) != 0 {
		veriftAccount = string(u.AssetApprove[transferAsset._TokenId])
	} else {
		veriftAccount = string(transferAsset._From)
	}
 
	var bccspInstance, _ = bccsp.NewBccsp()
	if transferAsset._SignType == "RSA" {
		//验签
		signBytes := HexToByte(transferAsset._Signature)
 
		// 验签的转换
		var hash bccsp.HashOpts
		var pubKey bccsp.Key
		rsaPubkey, err := getRSAPubkey(veriftAccount)
		if err != nil {
			return false, err
		}
		pubKey = rsaPubkey
 
		hash = &bccsp.SHA1Opts{}
		bytesUrl, _ := json.Marshal(transferAsset._Url)
		digest, err := bccspInstance.Hash(bytesUrl, hash)
 
		isVerify, err := bccspInstance.Verify(pubKey, signBytes, digest, &bccsp.RsaPKCS1V15Opts{Hash: crypto.SHA1})
		fmt.Println("isVerify:%s", isVerify)
		if err != nil {
			return false, err
		}
		fmt.Println("check sign success...")
		return true, nil
	} else {
		return true, nil
	}
}
 
//16进制字符串转[]byte
func HexToByte(hex string) []byte {
	length := len(hex) / 2
	slice := make([]byte, length)
	rs := []rune(hex)
 
	for i := 0; i < length; i++ {
		s := string(rs[i*2 : i*2+2])
		value, _ := strconv.ParseInt(s, 16, 10)
		slice[i] = byte(value & 0xFF)
	}
	return slice
}
 
func main() {
	err := shim.Start(new(Asset))
	if err != nil {
		fmt.Println("poe chaincode start error")
	}
	return
}

               

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

go语言实现NFT 的相关文章

  • 51单片机实战 1 --四个独立按键控制四位数码管

    本文基于普中51开发板 在其例程代码稍加改动而成的 单片机的入门小项目也很益智 启动单片机 四位数码管显示0000 按下s1并松开 显示1000 再按下s1并松开显示2000 连续10次按下并松开s2 数码管显示2100 2200 2300

随机推荐

  • WSL安装图形界面

    效果如下 1 下载并安装VcXsrv 链接如下 https sourceforge net projects vcxsrv 下载完安装一路next即可 或者自行选择安装路径 2 安装桌面环境 安装xfce4 terminalsudo apt
  • mysql的锁

    锁 锁机制用于管理对共享资源的并发访问 用来实现事务的隔离级别 锁类型 共享锁和排他锁都是行级锁 MySQL当中事务采用的是粒度锁 针对表 B 树 页 B 树叶子 节点 行 B 树叶子节点当中某一段记录行 三种粒度加锁 共享锁 S 可理解为
  • Python进阶-----面向对象2.0(特有属性和方法与私有属性和方法)

    目录 前言 1 添加特有属性 方法 示例1 添加特有属性 示例2 添加特有方法 2 私有属性 方法 1 私有化示例 2 私有化属性 方法可以在类的内部使用 3 强制访问私有化属性 方法 4 property装饰器去操作私有属性 方法 总结
  • 【测试入门】测试用例经典设计方法 —— 因果图法

    01 因果图设计测试用例的步骤 1 分析需求 阅读需求文档 如果User Case很复杂 尽量将它分解成若干个简单的部分 这样做的好处是 不必在一次处理过程中考虑所有的原因 没有固定的流程说明究竟分解到何种程度才算简单 需要测试人员根据自己
  • 【LeetCode-面试经典150题-day24】

    目录 35 搜索插入位置 74 搜索二维矩阵 162 寻找峰值 33 搜索旋转排序数组 35 搜索插入位置 题意 给定一个排序数组和一个目标值 在数组中找到目标值 并返回其索引 如果目标值不存在于数组中 返回它将会被按顺序插入的位置 请必须
  • 详解UART、I2C、SPI常用通信协议(全是细节)

    前言 UART I2C和SPI是我们在嵌入式开发中比较常见的通信协议了 没有最好的通信协议 每个通信协议都有自己的优缺点 如果想要通信速度快 SPI 将是理想的选择 如果用户想要连接多个设备而不是过于复杂 I2C 将是理想的选择 因为它最多
  • Java fail-fast与fail-safe

    fail fast和fail safe比较 java util包下面的所有的集合类都是快速失败的 而java util concurrent包下面的所有的类都是安全失败的 快速失败的迭代器会抛出ConcurrentModificationE
  • mac如何添加新的字体格式(以word中仿宋_GB2312为例)

    注意 字体中必须出现GB 2312格式的选项 才算成功
  • C#创建TCP服务器与客户端互传消息实例

    本项目使用C 语言建立一个TCP通讯实例 并可以互相传递消息 传送一下传智播客赵老师的视频课程 关键词解释 1 TCP协议 一种可以用于网络通信的数据传输协议 传输安全可靠不会有信息丢失 重点理解三次握手与四次分手 2 线程Thread 我
  • SQL2008 附加数据库提示 5120错误

    前几天在附加数据库时 出现了这个错误 在win7 x64系统上使用sql2008进行附加数据库 包括在x86系统正在使用的数据库文件 直接拷贝附加在X64系统中 时 提示无法打开文件 5120错误 这个错误是因为没有操作权限 所以附加的时候
  • 2018-02-07 如何记录日志

    一 简介 二 记录日志的目的 why 开发调试 记录用户行为 程序运行状况 系统 机器状况 三 日志的要素 what 时间 位置 级别 内容 唯一标识 事件上下文 格式化 其他 四 记录日志的一些原则和技巧 使用框架或模块 不能出错 避免敏
  • npm run build打包产生的build文件夹通过nginx部署到服务器上访问(centos8)

    首先在当前目录下 用npm run build命令将文件打包到build文件夹 或者是其他文件夹名 把build目录传到服务器上 打开终端 提一句 Windows在Microsoft store里新出的terminal应用还蛮好用的 当然是
  • 30分钟学会React Hook, Memo, Lazy

    我们来学习React 16 8里的新特性 1 自行配置好React的环境 推荐你使用Create React APP 你也可以下载本文档Zip解压到本地直接运行 https github com yurizhang fed study bl
  • python之用scapy分层解析pcap报文(Ethernet帧、IP数据包、TCP数据包、UDP数据包、Raw数据包)

    一 工具准备 下载安装scapy库 https blog csdn net qq 23977687 article details 88046257 安装完后 ls 命令可以查看所有支持的协议 ls IP 命令列出ip协议头部字段格式 只要
  • JDK线程池源码分析

    0 概述 线程池 从字面的含义上来看 是指管理一组工作线程 Worker Thread 的资源池 线程池是与工作队列 Work Queue 密切相关的 其中在工作队列中保存了需要执行的任务 工作线程很简单 从任务队列中取出一个任务 执行任务
  • 可解释的机器学习

    原文标题 Interpretable Machine Learning 作者 Parul Pandey 译者 intelLigenJ 算法工程师 鸢尾 编辑 王立鱼 原文链接 https towardsdatascience com int
  • [思维模式-5]:《如何系统思考》-1- 认识篇 - 总体结构与知识框架

    目录 前言 第一篇 认知篇 第1章 无所不在的系统 2 第2章 思维的转变 30 第二篇 方法与工具 第3章 深入思考 50 第4章 动态思考 78 第5章 全面思考 109 第6章 系统思考的 新语言 因果回路图 140 第7章 复杂背后
  • SpringCloud微服务项目快速搭建(SpringCloud Alibaba)

    一 概述 Spring Cloud Alibaba 是 Spring Cloud 和 Alibaba 面向微服务架构的一站式解决方案 为 Spring Cloud 生态中的各个组件提供了与 Alibaba 中间件的衔接 更方便 更易用的快速
  • Kali镜像下载

    http old kali org kali images 太全了 爱了爱了 存档
  • go语言实现NFT

    此文章以及链码 合约 均为原创 已通过以heco nft记录为NFT资产 HecoInfo Transaction Hash Txhash Details HecoInfo 请勿转载 如有兴趣 可扫描文章最后的二维码 基于erc721的以太