HyperledgerFabric资产案例链码实例

2023-10-29

案例分析

  • 功能
    • 用户开户和销户
    • 资产登记,资产上链,与具体的用户绑定
    • 资产转让,资产所有权变更
    • 查询功能,用户查询,资产查询,资产变更的历史查询
  • 业务实体
    • 用户
      • 名字
      • 身份证(标识)
      • 资产列表
    • 资产
      • 名字
      • 标识
      • 特殊属性列表(车:排量、品牌、座位)
  • 资产变更记录
    • 资产标识
    • 资产的原始拥有者
    • 资产变更后的拥有者
  • 交互方法
    • 用户开户
      • 参数:名字、标识
    • 用户销户
      • 参数:标识
    • 资产登记
      • 参数:名字、标识、特殊属性列表、拥有者
    • 资产转让
      • 参数:拥有者、资产标识、受让者
    • 用户查询
      • 参数:用户标识;返回值:用户实体
    • 资产查询
      • 参数:资产标识;返回值:资产实体
    • 资产的变更历史查询
      • 参数:资产标识;返回值:资产的变更记录列表

案例测试

  • 创建通道的创世交易(channel名字不能有大写)
    configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./config/assetschannel.tx -channelID assetschannel
  • 启动网络
    docker-compose up -d
  • 交互执行
    docker exec -it cli bash
  • 创建通道
    peer channel create -o orderer.example.com:7050 -c assetschannel -f /etc/hyperledger/config/assetschannel.tx
  • 加入通道(一个peer可以加入多个通道)
    peer channel join -b assetschannel.block
  • 安装链码
    peer chaincode install -n assets -v 1.0.0 -l golang -p github.com/chaincode/assetsExchange
  • 实例化链码
    peer chaincode instantiate -o orderer.example.com:7050 -C assetschannel -n assets -v 1.0.0 -l golang -c'{"Args":["init"]}'

测试方法

  • 用户开户
    peer chaincode invoke -C assetschannel -n assets -c '{"Args":["userRegister","user1","user1"]}'
  • 用户查询
    peer chaincode query -C assetschannel -n assets -c '{"Args":["queryUser","user1"]}'
    在这里插入图片描述
  • 资产登记
    peer chaincode invoke -C assetschannel -n assets -c '{"Args":["assetEnroll","asset1","asset1","metadate","user1"]}'
  • 资产查询
    peer chaincode query -C assetschannel -n assets -c '{"Args":["queryAsset","asset1"]}'
    在这里插入图片描述再查询用户,可以看到用户资产已经改变
    peer chaincode query -C assetschannel -n assets -c '{"Args":["queryUser","user1"]}'- 在这里插入图片描述
  • 资产转让
    新建用户user2
    peer chaincode invoke -C assetschannel -n assets -c '{"Args":["userRegister","user2","user2"]}'
    资产转让,user1的资产asset1转给user2
    peer chaincode invoke -C assetschannel -n assets -c '{"Args":["assetExchange","user1","asset1","user2"]}'
  • 查询资产变更历史
    peer chaincode query -C assetschannel -n assets -c '{"Args":["queryAssetHistory","asset1"]}'
  • 删除用户,用户的资产会被同时删除
    peer chaincode invoke -C assetschannel -n assets -c '{"Args":["userDestroy","user2"]}'

案例代码

package main

import (
	"encoding/json"
	"fmt"
	"github.com/hyperledger/fabric/core/chaincode/shim"
	pb "github.com/hyperledger/fabric/protos/peer"
)

//定义链码
type AssetsExchangeCC struct {
}

//资产默认的原始拥有者
const (
	originOwner = "originOwnerPlaceholder"
)

//资产
type Asset struct {
	Name     string `json:"name"`
	Id       string `json:"id"`
	Metadata string `json:"metadata"`
}

//用户
type User struct {
	Name   string   `json:"name"`
	Id     string   `json:"id"`
	Assets []string `json:"assets"`
}

//资产变更记录
type AssetHistory struct {
	//资产标识
	AssetId string `json:"asset_id"`
	//资产的原始拥有者
	OriginOwnerId string `json:"origin_owner_id"`
	//变更后的拥有者
	CurrentOwnerId string `json:"current_owner_id"`
}

//链码的初始化
func (c *AssetsExchangeCC) Init(stub shim.ChaincodeStubInterface) pb.Response {
	return shim.Success(nil)
}

//链码交互
func (c *AssetsExchangeCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
	//得到方法名
	funcName, args := stub.GetFunctionAndParameters()
	//根据不同方法去判断
	switch funcName {
	case "userRegister":
		//用户开户
		return userRegister(stub, args)
	case "userDestroy":
		//用户销户
		return userDestroy(stub, args)
	case "assetEnroll":
		//资产登记
		return assetEnroll(stub, args)
	case "assetExchange":
		//资产转让
		return assetExchange(stub, args)
	case "queryUser":
		//用户查询
		return queryUser(stub, args)
	case "queryAsset":
		//资产查询
		return queryAsset(stub, args)
	case "queryAssetHistory":
		//资产变更历史查询
		return queryAssetHistory(stub, args)
	default:
		return shim.Error(fmt.Sprintf("不支持的方法:%s", funcName))
	}
}

//用户开户
func userRegister(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	//判断个数必须为2个
	if len(args) != 2 {
		return shim.Error("参数个数错误")
	}
	//判断传过来的参数是a否为空
	name := args[0]
	id := args[1]
	if name == "" || id == "" {
		return shim.Error("无效的参数")
	}
	//判断用户是否存在,若存在,则报错
	if userBytes, err := stub.GetState(constructUserKey(id)); err == nil && len(userBytes) != 0 {
		return shim.Error("用户已存在")
	}

	//写入世界状态,传过来的是用户的名字和id,绑定User结构体 make([]string,0)
	user := &User{
		Name:   name,
		Id:     id,
		Assets: make([]string, 0),
	}
	//序列化
	userBytes, err := json.Marshal(user)
	if err != nil {
		return shim.Error(fmt.Sprintf("序列化与用户失败 %s", err))
	}

	//将对象状态写入数据库
	if err := stub.PutState(constructUserKey(id), userBytes); err != nil {
		return shim.Error(fmt.Sprintf("存入用户失败 %s", err))
	}
	//返回成功
	return shim.Success(nil)
}
func constructUserKey(userId string) string {
	return fmt.Sprintf("user_%s", userId)
}

//用户销户
func userDestroy(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	//参数个数1个
	if len(args) != 1 {
		return shim.Error("参数个数不对")
	}
	//校验参数正确性
	id := args[0]
	if id == "" {
		return shim.Error("无效的参数")
	}
	//判断用户是否存在
	userBytes, err := stub.GetState(constructUserKey(id))
	if err != nil && len(userBytes) == 0 {
		return shim.Error("找不到用户")
	}

	//写入状态
	if err := stub.DelState(constructUserKey(id)); err != nil {
		return shim.Error(fmt.Sprintf("删除用户失败 %s", err))
	}

	//删除用户名下的资产
	user := new(User)
	if err := json.Unmarshal(userBytes, user); err != nil {
		return shim.Error(fmt.Sprintf("反序列化失败 %s", err))
	}
	for _, assetid := range user.Assets {
		if err := stub.DelState(constructAssetKey(assetid)); err != nil {
			return shim.Error(fmt.Sprintf("删除资产失败 %s", err))
		}
	}
	return shim.Success(nil)
}

//使用组合键来区分
//所有的资产,用asset开头
func constructAssetKey(assetId string) string {
	return fmt.Sprintf("asset_%s", assetId)
}

//资产登记
func assetEnroll(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	if len(args) != 4 {
		return shim.Error("参数个数不对")
	}
	//验证正确性
	assetName := args[0]
	assetId := args[1]
	metadata := args[2]
	ownerId := args[3]
	//metadata可以为空
	if assetName == "" || assetId == "" || ownerId == "" {
		return shim.Error("无效的参数")
	}
	//验证拥有者是否存在,拥有者必须存在
	userBytes, err := stub.GetState(constructUserKey(ownerId))
	if err != nil || len(userBytes) == 0 {
		return shim.Error("找不到用户")
	}
	//验证资产是否存在,资产必须不存在
	if assetBytes, err := stub.GetState(constructAssetKey(assetId)); err == nil && len(assetBytes) != 0 {
		return shim.Error("资产已经存在")
	}

	//写入状态
	asset := &Asset{
		Name:     assetName,
		Id:       assetId,
		Metadata: metadata,
	}
	//序列化
	assetBytes, err := json.Marshal(asset)
	if err != nil {
		return shim.Error(fmt.Sprintf("序列化失败 %s", err))
	}
	//保存资产
	if err := stub.PutState(constructAssetKey(assetId), assetBytes); err != nil {
		return shim.Error(fmt.Sprintf("保存失败 %s", err))
	}

	//拥有者
	user := new(User)
	if err := json.Unmarshal(userBytes, user); err != nil {
		return shim.Error(fmt.Sprintf("反序列化失败 %s", err))
	}

	user.Assets = append(user.Assets, assetId)
	if userBytes, err = json.Marshal(user); err != nil {
		return shim.Error(fmt.Sprintf("序列化用户失败 %s", err))
	}
	//存储用户状态
	if err := stub.PutState(constructUserKey(user.Id), userBytes); err != nil {
		return shim.Error(fmt.Sprintf("保存用户失败 %s", err))
	}

	//资产历史变更
	history := &AssetHistory{
		AssetId:        assetId,
		OriginOwnerId:  originOwner,
		CurrentOwnerId: ownerId,
	}
	historyBytes, err := json.Marshal(history)
	if err != nil {
		return shim.Error(fmt.Sprintf("序列化失败 %s", err))
	}

	//使用fabric内置的组合键机制
	historyKey, err := stub.CreateCompositeKey("history", []string{
		assetId,
		originOwner,
		ownerId,
	})
	if err != nil {
		return shim.Error(fmt.Sprintf("创建key失败 %s", err))
	}

	//资产变更存储
	if err := stub.PutState(historyKey, historyBytes); err != nil {
		return shim.Error(fmt.Sprintf("保存变更历史失败 %s", err))
	}

	return shim.Success(nil)
}

//资产转让
func assetExchange(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	//参数个数为3个
	if len(args) != 3 {
		return shim.Error("参数个数不对")
	}
	//参数校验
	ownerId := args[0]
	assetId := args[1]
	currentOwnerId := args[2]
	if ownerId == "" || assetId == "" || currentOwnerId == "" {
		return shim.Error("无效的参数")
	}
	//验证当前和受让后的用户是否存在
	originOwnerBytes, err := stub.GetState(constructUserKey(ownerId))
	if err != nil || len(originOwnerBytes) == 0 {
		return shim.Error("用户找不到")
	}
	currentOwnerBytes, err := stub.GetState(constructUserKey(currentOwnerId))
	if err != nil || len(currentOwnerBytes) == 0 {
		return shim.Error("用户找不到")
	}
	//验证资产存在
	assetBytes, err := stub.GetState(constructAssetKey(assetId))
	if err != nil || len(assetBytes) == 0 {
		return shim.Error("资产找不到")
	}

	//校验原始拥有者确实拥有当前变更的资产
	originOwner := new(User)
	if err := json.Unmarshal(originOwnerBytes, originOwner); err != nil {
		return shim.Error(fmt.Sprintf("反序列化失败 %s", err))
	}

	//定义标记,标识资产是否存在
	aidexist := false
	for _, aid := range originOwner.Assets {
		if aid == assetId {
			//若找到该资产,则变更状态,结束循环
			aidexist = true
			break
		}
	}

	if !aidexist {
		return shim.Error("资产所有者不匹配")
	}

	//写入状态
	//1.将资产的原始拥有者资产id删除
	//2.新拥有者写入资产id,资产绑定
	//3.资产变更记录

	assetIds := make([]string, 0)
	for _, aid := range originOwner.Assets {
		if aid == assetId {
			//遍历到了要转让的资产
			continue
		}
		assetIds = append(assetIds, aid)
	}

	originOwner.Assets = assetIds
	//序列化
	originOwnerBytes, err = json.Marshal(originOwner)
	if err != nil {
		return shim.Error(fmt.Sprintf("序列化失败 %s", err))
	}

	//存储原始拥有者
	if err := stub.PutState(constructUserKey(ownerId), originOwnerBytes); err != nil {
		return shim.Error(fmt.Sprintf("存储用户失败 %s", err))
	}

	//当前拥有者
	currentOwner := new(User)
	if err := json.Unmarshal(currentOwnerBytes, currentOwner); err != nil {
		return shim.Error(fmt.Sprintf("反序列化失败 %s", err))
	}

	//绑定资产
	currentOwner.Assets = append(currentOwner.Assets, assetId)
	currentOwnerBytes, err = json.Marshal(currentOwner)
	if err != nil {
		return shim.Error(fmt.Sprintf("序列化失败 %s", err))
	}
	//存储
	if err := stub.PutState(constructUserKey(currentOwnerId), currentOwnerBytes); err != nil {
		return shim.Error("保存用户失败")
	}

	//插入资产变更记录
	history := &AssetHistory{
		AssetId:        assetId,
		OriginOwnerId:  ownerId,
		CurrentOwnerId: currentOwnerId,
	}

	historyBytes, err := json.Marshal(history)
	if err != nil {
		return shim.Error(fmt.Sprintf("序列化失败 %s", err))
	}
	historyKey, err := stub.CreateCompositeKey("history", []string{
		assetId,
		ownerId,
		currentOwnerId,
	})
	if err != nil {
		return shim.Error(fmt.Sprintf("创建key失败 %s", err))
	}
	//存储历史变更记录
	if err := stub.PutState(historyKey, historyBytes); err != nil {
		return shim.Error(fmt.Sprintf("保存失败 %s", err))
	}
	return shim.Success(nil)
}

//用户查询
func queryUser(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	//参数个数1个
	if len(args) != 1 {
		return shim.Error("参数个数不对")
	}

	//校验正确性
	ownerId := args[0]
	if ownerId == "" {
		return shim.Error("无效的参数")
	}

	userBytes, err := stub.GetState(constructUserKey(ownerId))
	if err != nil || len(userBytes) == 0 {
		return shim.Error("找不到用户")
	}
	return shim.Success(userBytes)
}

//资产查询
func queryAsset(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	//参数个数1个
	if len(args) != 1 {
		return shim.Error("参数个数不对")
	}

	//校验正确性
	assetId := args[0]
	if assetId == "" {
		return shim.Error("无效的参数")
	}

	assetBytes, err := stub.GetState(constructAssetKey(assetId))
	if err != nil || len(assetBytes) == 0 {
		return shim.Error("找不到资产")
	}
	return shim.Success(assetBytes)
}

//资产历史变更查询
func queryAssetHistory(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	//参数个数1个
	if len(args) != 1 && len(args) != 2 {
		return shim.Error("参数个数不对")
	}

	//校验参数的正确性
	assetId := args[0]
	if assetId == "" {
		return shim.Error("无效的参数")
	}

	//queryType:all
	//默认为all
	queryType := "all"
	if len(args) == 2 {
		//变为用户传的值
		queryType = args[1]
	}

	//参数校验
	if queryType != "all" && queryType != "exchange" && queryType != "enroll" {
		return shim.Error(fmt.Sprintf("未知的查询类型 %s", queryType))
	}

	//校验资产是否存在
	assetBytes, err := stub.GetState(constructAssetKey(assetId))
	if err != nil || len(assetBytes) == 0 {
		return shim.Error("资产找不到")
	}

	keys := make([]string, 0)
	keys = append(keys, assetId)
	switch queryType {
	case "enroll":
		//资产登记
		keys = append(keys, originOwner)
	case "exchange", "all":
	default:
		return shim.Error(fmt.Sprintf("不支持的类型 %s", queryType))
	}

	//组合键
	//得到迭代器
	result, err := stub.GetStateByPartialCompositeKey("history", keys)
	if err != nil {
		return shim.Error(fmt.Sprintf("查询历史错误 %s", err))
	}

	//关闭
	defer result.Close()

	histories := make([]*AssetHistory, 0)
	for result.HasNext() {
		historyVal, err := result.Next()
		if err != nil {
			return shim.Error(fmt.Sprintf("查询错误 %s", err))
		}

		history := new(AssetHistory)
		if err := json.Unmarshal(historyVal.GetValue(), history); err != nil {
			return shim.Error(fmt.Sprintf("反序列化失败 %s", err))
		}

		//过滤,不是资产转让的记录过滤
		if queryType == "exchange" && history.OriginOwnerId == originOwner {
			continue
		}
		histories = append(histories, history)
	}

	historiesBytes, err := json.Marshal(histories)
	if err != nil {
		return shim.Error(fmt.Sprintf("序列化失败 %s", err))
	}
	return shim.Success(historiesBytes)
}

func main() {
	err := shim.Start(new(AssetsExchangeCC))
	if err != nil {
		fmt.Println("启动链码失败")
	}
}

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

HyperledgerFabric资产案例链码实例 的相关文章

  • 文储研习社第17期

    文储研习社是文储区块链技术人员自发组织的学习交流社区 旨在于追踪区块链时下最新热点 解码热点蕴含的未知领域 享受思想交流的碰撞 欢迎志同道合的小伙伴加入我们 共同学习与成长 第17期 为了提高考证通过率 不小心搭了条链 作者 Bingo 你
  • 区块链技术及应用概述

    一 基本概念 什么是区块链 区块链是一种以密码学方式保证的不可篡改和不可伪造的分布式账本 关键特点 去中心化 不可篡改性 匿名性 安全可信 区块链架构 1 数据层 主要描述区块链系统的物理形式 它是从Genesis区块开始的区块链链结构 包
  • 以太坊区块链学习之在私链上部署合约

    上一篇博客介绍了如何搭建私链并在私链上创建账户 挖矿 查看余额 本篇将介绍在私链上部署合约并与之交互 本篇开发环境为MacOS 10 12 建议读者使用macOS系统或者Ubuntu系统 第一步 进入geth客户端 启动私链 进入geth客
  • 2018年区块链人才趋势:降温、调节、蓄势待发

    2018年 戊戌年 是一个不平静的年份 自960年前的戊戌年王安石变法 到最近两百年的四个戊戌年 其间发生的大事无不与 变革 息息相关 年初 从科技圈 金融圈里 引爆出一个叫 区块链 的公众热点 从技术角度而言 这并非新生事物 但因其巨大的
  • 巴比特

    摘要 3月15日凌晨 OpenAI在官网上宣告了多模态大模型GPT 4的诞生 GPT 4 实现了以下几个方面的飞跃式提升 强大的识图能力 文字输入限制提升至 2 5 万字 回答准确性显著提高 能够生成歌词 创意文本 实现风格变化 GPT 4
  • Go语言实现区块链与加密货币-Part3(交易优化,单机模拟多节点通信)

    交易 二 在这个系列文章的一开始 我们就提到了 区块链是一个分布式数据库 不过在之前的文章中 我们选择性地跳过了 分布式 这个部分 而是将注意力都放到了 数据库 部分 到目前为止 我们几乎已经实现了一个区块链数据库的所有元素 今天 我们将会
  • 区块链技术是如何应用到版权维护上?

    随着视频和音乐行业的迅速发展 数字出版已经形成完整的产业链 带来一些可观的收入 但是也伴随侵权的现象发生 那么区块链技术怎么运用到作品版权保护上呢 1 时间戳 我们知道区块链有一个 时间戳 这个可信时间戳由权威机构签发 能证明数据电文在一个
  • 阿里巴巴都害怕的区块链电商到底是什么?

    近日 区块链权威机构中国通信工业协会区块链专业委员会 CCIAPCB 发出倡议 联合各界将中共中央政治局10月24日集体学习区块链主席讲话日作为 区块链中国日 此次中央将区块链技术放在了国家战略层面高度上 让区块链一时间成了全民热点 特别是
  • Bridge Champ举办人机对战赛:NFT游戏与传统竞技共生发展编织新格局

    概要 现在 NFT与体育竞技正日益紧密地联系在一起 一些体育项目开始推出与赛事或球队相关的NFT 同时也有部分NFT游戏开始举办电子竞技赛事 这种共生发展正在改变体育竞技的生态 笔者采访了桥牌冠军项目相关负责人 探讨NFT游戏与传统体育竞技
  • 区块链+物联网 BOT

    不可否认 我们的一只脚已经迈入万物智联时代 但另一只脚迈入还存在一定的阻碍 区块链技术的出现将会促进这一进程的发展 智能音响 主人你好 我是小Q 现在是早上08点29分 上班时间要到咯 智能门锁 主人你摔疼我了 你总是这样匆忙 下次赶紧麻溜
  • 自己动手部署以太坊联盟链

    一个区块链学习项目 GitHub https github com xianfeng92 Love Ethereum 假设已经在Ubunbu 14 04 LTS上安装好了以太坊客户端Geth 使用Geth部署以太坊联盟链 以太坊Geth客户
  • 区块链中的哈希算法

    区块链中的密码学 密码学在区块链中的应用主要有两个 哈希算法与非对称加密算法 这次主要对哈希算法进行详细的说明 哈希算法 哈希算法的特点有 1 输入可以为任意大小的字符串 2 产生固定大小的输出 3 可以在合理的时间内算出输出值 若要满足密
  • hyperledger fabric介绍

    一 Hyperledger Fabric介绍 2015年 Linux基金会启动了Hyperledger项目 目标是发展跨行业的区块链技术 Hyperledger Fabric是Hyperledger中的一个区块链项目 包含一个账本 使用智能
  • Cumulus Encrypted Storage System(CESS)激励测试网 v0.7.5 于11月29日正式上线

    Cumulus Encrypted Storage System CESS 是基于区块链的去中心化云存储网络和 CDN 网络 支持数据在线存储和实时共享 为 Web3 高频动态数据的存储和检索提供全栈解决方案 CESS 数据价值网络是以 D
  • 扬帆证券:玻璃期价涨势强劲 投资者需理性看待

    上个交易周 国内产品期货商场全体工作平稳 其间 玻璃期货体现较为出色 主力合约上星期五午后忽然大幅拉升 毕竟收涨逾7 周内累计涨幅超越10 业内人士以为 近期玻璃期价走势强劲主要是受地产政策利好和本钱增加的推动 后市行情或将偏震动 上涨持续
  • 什么是虚值期权?什么是深度虚值期权?

    在期权市场里有一种合约的价格往往比较便宜 它就是虚值期权 也是最受欢迎的期权之一 虚值期权也就是高杠杆和波动被世人所爱 久闻的一日192倍行情就是出自于虚值期权 下文科普什么是虚值期权 什么是深度虚值期权 一 虚值期权是什么 虚值合约因为价
  • 在区块链中看CHAT的独特见解

    问CHAT 谈谈对区块链以及区块链金融的理解 CHAT回复 区块链是一种去中心化的分布式数据库技术 这种技术通过加密算法 使数据在网络中传输和存储的过程变得更加安全可靠 区块链的出现引领了存储 交易等形式的革命 改变了诸多行业的运作模式 首
  • 一文了解Substrate

    Substrate是用于构建特定类型区块链的工具箱 它本身并不是一个区块链 而是开发者用来创建独特而强大的区块链的一套工具 Substrate允许开发者可以自己创建所需所想功能的区块链 无需受限于现有的设计 Substrate使用FRAME
  • 【网络安全】——区块链安全和共识机制

    区块链安全和共识机制 摘要 区块链技术作为一种分布式去中心化的技术 在无需第三方的情况下 使得未建立信任的交易双方可以达成交易 因此 区块链技术近年来也在金融 医疗 能源等多个行业得到了快速发展 然而 区块链为无信任的网络提供保障的同时 也
  • 案例研究:YGG 如何通过 GAP 帮助 Pixels 扩大玩家群体

    在 Sky Mavis 联合创始人 Jeffrey Jihoz Zirlin 在 YGG Web3 游戏峰会 W3GS 上发表主题演讲时 他向在场的人们透露 MMO 农场游戏 Pixels 的日活跃用户数已经超过了 130 000 人 这使

随机推荐

  • 音视频基础之封装格式与音视频同步

    封装格式的概念 封装格式 也叫容器 就是将已经编码压缩好的视频流 音频流及字幕按照一定的方案放到一个文件中 便于播放软件播放 一般来说 视频文件的后缀名就是它的封装格式 封装的格式不一样 后缀名也就不一样 比如 同样的陷可以做成饺子也可以做
  • php 定义float,MySql中float类型含义及参数详解

    php 定义float MySql中float类型含义及参数详解 float表示浮点数 通俗点来说的话 我们可以简单理解为小数 参数有两个 M表示精度 表示浮点数的位数 D表示标度 表示小数位数 M位数不包括小数点位数 举例 float 6
  • Excel如何将引用的sheet名称全部替换。

    一 设置引用sheet名称 例如为星期2 输入公式 引用A2行内容 星期2 A2 符号为固定单元格 二 将表格3星期3的内容引用到星期一表格中 三 将星期3内容引用到星期一 星期3 A2 如何将星期2全部换成星期3呢 四 将星期1的复制到新
  • 智能制造MES系统的主要内容有哪些?系统有什么作用?

    制造企业非常关注实现生产过程中的实时采集 提高生产排产的效率 实现制造过程的追溯 提升工人与设备的绩效 保证产品质量等问题 调研数据显示 92 的企业渴望加强对生产过程的控制 大多数制造企业已经逐渐清醒地认识到生产技术领先和制造过程管理高效
  • Linux操作系统常见面试题(持续更新)

    1 熟悉命令netstat tcpdump ipcs ipcrm netstat 检查网络状态 tcpdump 截获数据包 ipcs 检查共享内存 ipcrm 解除共享内存 2 共享内存段被映射进进程空间之后 存在于进程空间的什么位置 共享
  • uni-app在真机调试下兼容ethers的方法

    目录 一 安装ethers 二 renderjs 三 注意事项 uni app开发跨平台应用程序 项目搭建主要前端框是Uni app Vue3 TS Vite 项目搭建参考文章Uni app Vue3 TS Vite 创建项目 Hbuild
  • tac_plus安装和配置

    安装 将 http download csdn net detail wingking84 5814131 解压 PROJECTS放到 root下 进入PROJECTS然后执行 make make install 配置 将参考配置文件 ht
  • 英国加密货币流动性提供商获得金融监管机构批准

    点击上方 蓝色字 可关注我们 暴走时评 根据金融行为监管局 FCA 的注册记录 英国加密货币流动性创业公司B2C2 OTC Ltd 已于1月30日获得该国FCA的批准 B2C2将提供电子场外交易 OTC 可以向合格交易方和专业客户提供差价合
  • CUDA——SM中warp调度器调度机制&&访存延迟隐藏

    SM中warp调度器调度机制 访存延迟隐藏 核函数中并不是所有线程一起启动执行的 核函数的执行是以线程束 warps 作为单位 warps的执行由warp调度器进行调度 一个调度器只能调度一个warp去执行指令 一个warp里的所有线程几乎
  • Symbol 'ANDROID_LOG_DEBUG' could not be resolved

    调试JNI代码的时候 加入了调试函数 include
  • 如何构造LL(1)文法预测分析表

    这类题型也经常在考试中出现 一般是与判断是否为LL 1 文法放在一起进行考察 这类题目该怎么去做呢 1 求出每个非终结符的FIRST集和FOLLOW集 在上一篇文章中已经详细介绍 2 构造预测分析表 横坐标是所有的非终结符 纵坐标是所有的终
  • 【计算机视觉

    文章目录 一 STPLS3D 二 DigestPath 三 ImageNet S ImageNet Semantic Segmentation 四 OpenEDS 五 RELLIS 3D 六 SUIM Segmentation of Und
  • React解密:React Hooks函数之useEffect和useLayoutEffect

    useEffect是react hooks的又一个重要的hooks函数 Effect hooks允许你在组件中执行副作用操作 数据获取 设置订阅以及手动更改 React 组件中的 DOM 都属于副作用 不管你知不知道这些操作 或是 副作用
  • js事件的绑定方式

    我们现在绑定的事件都是 onxxxx的方式 这个是DOM0级的事件绑定方式 注 这个方式不是很好 弊端 一旦写了第二个事件 那么第一个就会被覆盖 案例 var oDiv document querySelector div oDiv onc
  • gethup.sh

    docker exec it geth cluster1 bin bash geth datadir data0 networkid 779977 console root 85547cf26bca ethutil cat gethup s
  • 某易云音乐JS逆向案例

    某易云音乐参数破解 目标 aHR0cHM6Ly9tdXNpYy4xNjMuY29tLyMvc2VhcmNoL20vP3M9JUU2JTg4JTkwJUU5JTgzJUJEJnR5cGU9MQ 某易云音乐是大家喜爱的音乐平台 有小伙伴问我 这
  • 网关的理解

    一 什么是网关 网关 Gateway 又称网间连接器 协议转换器 网关在传输层上以实现网络互连 是最复杂的网络互连设备 仅用于两个高层协议不同的网络互连 二 如何来理解网关 大家都知道 从一个房间走到另一个房间 必然要经过一扇门 同样 从一
  • Qt自绘控件之扇形统计图

    首先绘制区域扇形需要先注意一下几点 QPainter中绘制完整的圆等于5760 16 360 此处数值用于计算每一块扇形区域所显示的 需要了解一下扇形二等分线的计算方法 要注意做坐标原点转换 此处为屏幕分辨率自适应 const qreal
  • Keil 中出现“encountered an improper argument” 解决办法

    Keil 中出现 encountered an improper argument 解决办法 出现这种情况就是因为目录文件下带有中文路径 不要弄成中文路径就可以解决了
  • HyperledgerFabric资产案例链码实例

    案例分析 功能 用户开户和销户 资产登记 资产上链 与具体的用户绑定 资产转让 资产所有权变更 查询功能 用户查询 资产查询 资产变更的历史查询 业务实体 用户 名字 身份证 标识 资产列表 资产 名字 标识 特殊属性列表 车 排量 品牌