超级账本项目:链码示例
链码示例一:信息公证
简介
chaincode_example01.go 主要实现如下的功能:
- 初始化,以键值形式存放信息;
- 允许读取和修改键值。
代码中,首先初始化了 hello_world
的值,并根据请求中的参数创建修改查询链上 key
中的值,本质上实现了一个简单的可修改的键值数据库。
主要函数
-
read
:读取keyargs[0]
的 value; -
write
:创建或修改 keyargs[0]
的 value; -
init
:初始化 keyhello_world
的 value; -
invoke
:根据传递参数类型调用执行相应的init
和write
函数; -
query
:调用read
函数查询args[0]
的 value。
代码运行分析
main
函数作为程序的入口,调用 shim 包的 start 函数,启动 chaincode 引导程序的入口节点。如果报错,则返回。
func main() {
err := shim.Start(new(SimpleChaincode))
if err != nil {
fmt.Printf("Error starting Simple chaincode: %s", err)
}
}
当智能合约部署在区块链上,可以通过 rest api 进行交互。
三个主要的函数是 init
,invoke
,query
。在三个函数中,通过 stub.PutState
与 stub.GetState
存储访问 ledger 上的键值对。
通过 REST API 操作智能合约
假设以 jim 身份登录 pbft 集群,请求部署该 chaincode 的 json 请求格式为:
{
"jsonrpc": "2.0",
"method": "deploy",
"params": {
"type": 1,
"chaincodeID": {
"path": "https://github.com/ibm-blockchain/learn-chaincode/finished"
},
"ctorMsg": {
"function": "init",
"args": [
"hi there"
]
},
"secureContext": "jim"
},
"id": 1}
目前 path 仅支持 github 上的目录,ctorMsg 中为函数 init
的传参。
调用 invoke 函数的 json 格式为:
{
"jsonrpc": "2.0",
"method": "invoke",
"params": {
"type": 1,
"chaincodeID": {
"name": "4251b5512bad70bcd0947809b163bbc8398924b29d4a37554f2dc2b033617c19cc0611365eb4322cf309b9a5a78a5dba8a5a09baa110ed2d8aeee186c6e94431"
},
"ctorMsg": {
"function": "init",
"args": [
"swb"
]
},
"secureContext": "jim"
},
"id": 2}
其中 name 字段为 deploy
后返回的 message 字段中的字符串。
query
的接口也是类似的。
链码示例二:交易资产
简介
chaincode_example02.go 主要实现如下的功能:
- 初始化 A、B 两个账户,并为两个账户赋初始资产值;
- 在 A、B 两个账户之间进行资产交易;
- 分别查询 A、B 两个账户上的余额,确认交易成功;
- 删除账户。
主要函数
-
init
:初始化 A、B 两个账户; -
invoke
:实现 A、B 账户间的转账; -
query
:查询 A、B 账户上的余额; -
delete
:删除账户。
依赖的包
import (
"errors"
"fmt"
"strconv"
"github.com/hyperledger/fabric/core/chaincode/shim"
)
strconv
实现 int 与 string 类型之间的转换。
在invoke 函数中,存在:
X, err = strconv.Atoi(args[2])
Aval = Aval - X
Bval = Bval + X
当 args[2]<0
时,A 账户余额增加,否则 B 账户余额减少。
可扩展功能
实例中未包含新增账户并初始化的功能。开发者可以根据自己的业务模型进行添加。
数字货币发行与管理
简介
该智能合约实现一个简单的商业应用案例,即数字货币的发行与转账。在这之中一共分为三种角色:中央银行,商业银行,企业。其中中央银行可以发行一定数量的货币,企业之间可以进行相互的转账。主要实现如下的功能:
- 初始化中央银行及其发行的货币数量
- 新增商业银行,同时央行并向其发行一定数量的货币
- 新增企业
- 商业银行向企业转给一定数量的数字货币
- 企业之间进行相互的转账
- 查询企业、银行、交易信息
主要函数
-
init
:初始化中央银行,并发行一定数量的货币; -
invoke
:调用合约内部的函数; -
query
:查询相关的信息; -
createBank
:新增商业银行,同时央行向其发行一定数量的货币; -
createCompany
:新增企业; -
issueCoin
:央行再次发行一定数量的货币(归于交易); -
issueCoinToBank
:央行向商业银行转一定数量的数字货币(归于交易); -
issueCoinToCp
:商业银行向企业转一定数量的数字货币(归于交易行为); -
transfer
:企业之间进行相互转账(归于交易行为); -
getCompanys
:获取所有的公司信息,如果企业个数大于10,先访问前10个; -
getBanks
:获取所有的商业银行信息,如果商业银行个数大于10,先访问前 10 个 -
getTransactions
:获取所有的交易记录 如果交易个数大于10,先访问前 10 个; -
getCompanyById
:获取某家公司信息; -
getBankById
:获取某家银行信息; -
getTransactionBy
:获取某笔交易记录; -
writeCenterBank
:修改央行信息; -
writeBank
:修改商业银行信息; -
writeCompany
:修改企业信息; -
writeTransaction
:写入交易信息。
数据结构设计
- centerBank 中央银行
- Name:名称
- TotalNumber:发行货币总数额
- RestNumber:账户余额
- ID:ID固定为 0
- bank 商业银行
- Name:名称
- TotalNumber:收到货币总数额
- RestNumber:账户余额
- ID:银行 ID
- company 企业
- Name:名称
- Number:账户余额
- ID:企业 ID
- transaction 交易内容
- FromType:发送方角色 //centerBank:0,Bank:1,Company:2
- FromID:发送方 ID
- ToType:接收方角色 //Bank:1,Company:2
- ToID:接收方 ID
- Time:交易时间
- Number:交易数额
- ID:交易 ID
接口设计
init
request 参数:
args[0] 银行名称
args[1] 初始化发布金额
response 参数:
{"Name":"XXX","TotalNumber":"0","RestNumber":"0","ID":"XX"}
createBank
request 参数:
args[0] 银行名称
response 参数:
{"Name":"XXX","TotalNumber":"0","RestNumber":"0","ID":"XX"}
createCompany
request 参数:
args[0] 公司名称
response 参数:
{"Name":"XXX","Number":"0","ID":"XX"}
issueCoin
request 参数:
args[0] 再次发行货币数额
response 参数:
{"FromType":"0","FromID":"0","ToType":"0","ToID":"0","Time":"XX","Number":"XX","ID":"XX"}
issueCoinToBank
request 参数:
args[0] 商业银行ID
args[1] 转账数额
response 参数:
{"FromType":"0","FromID":"0","ToType":"1","ToID":"XX","Time":"XX","Number":"XX","ID":"XX"}
issueCoinToCp
request 参数:
args[0] 商业银行ID
args[1] 企业ID
args[2] 转账数额
response 参数:
{"FromType":"1","FromID":"XX","ToType":"2","ToID":"XX","Time":"XX","Number":"XX","ID":"XX"}
transfer
request 参数:
args[0] 转账用户ID
args[1] 被转账用户ID
args[2] 转账余额
response 参数:
{"FromType":"2","FromID":"XX","ToType":"2","ToID":"XX","Time":"XX","Number":"XX","ID":"XX"}
getBanks
response 参数
[{"Name":"XXX","Number":"XX","ID":"XX"},{"Name":"XXX","Number":"XX","ID":"XX"},...]
getCompanys
response 参数
[{"Name":"XXX","TotalNumber":"XX","RestNumber":"XX","ID":"XX"},{"Name":"XXX","TotalNumber":"XX","RestNumber":"XX","ID":"XX"},...]
getTransactions
response 参数
[{"FromType":"XX","FromID":"XX","ToType":"XX","ToID":"XX","Time":"XX","Number":"XX","ID":"XX"},{"FromType":"XX","FromID":"XX","ToType":"XX","ToID":"XX","Time":"XX","Number":"XX","ID":"XX"},...]
getCenterBank
response 参数
[{"Name":"XX","TotalNumber":"XX","RestNumber":"XX","ID":"XX"}]
getBankById
request 参数
args[0] 商业银行ID
response 参数
[{"Name":"XX","TotalNumber":"XX","RestNumber":"XX","ID":"XX"}]
getCompanyById
request 参数
args[0] 企业ID
response 参数
[{"Name":"XXX","Number":"XX","ID":"XX"}]
getTransactionById
request 参数
args[0] 交易ID
response 参数
{"FromType":"XX","FromID":"XX","ToType":"XX","ToID":"XX","Time":"XX","Number":"XX","ID":"XX"}
writeCenterBank
request 参数
CenterBank
response 参数
err nil 为成功
writeBank
request 参数
Bank
response 参数
err nil 为成功
writeCompany
request 参数
Company
response 参数
err nil 为成功
writeTransaction
request 参数
Transaction
response 参数
``` err nil 为成功 ···
其它
查询时为了兼顾读速率,将一些信息备份存放在非区块链数据库上也是一个较好的选择。
学历认证
功能描述
该 智能合约 实现了一个简单的征信管理的案例。针对于学历认证领域,由于条约公开,在条约外无法随意篡改的特性,天然具备稳定性和中立性。
该智能合约中三种角色如下:
- 学校
- 个人
- 需要学历认证的机构或公司
学校可以根据相关信息在区块链上为某位个人授予学历,相关机构可以查询某人的学历信息,由于使用私钥签名,确保了信息的真实有效。 为了简单,尽量简化相关的业务,另未完成学业的学生因违纪或外出创业退学,学校可以修改其相应的学历信息。
账户私钥应该由安装在本地的客户端生成,本例中为了简便,使用模拟私钥和公钥。
数据结构设计
- 学校
- 名称
- 所在位置
- 账号地址
- 账号公钥
- 账户私钥
- 学校学生
- 个人
- 姓名
- 账号地址
- 过往学历
- 学历信息
- 学历信息编号
- 就读学校
- 就读年份
- 完成就读年份
- 就读状态 //0:毕业 1:退学
- 修改记录(入学也相当于一种修改记录)
- 编号
- 学校账户地址(一般根据账户地址可以算出公钥地址,然后可以进行校验)
- 学校签名
- 个人账户地址
- 个人公钥地址(个人不需要公钥地址)
- 修改时间
- 修改操作//0:正常毕业 1:退学 2:入学
对学历操作信息所有的操作都归为记录。
function及各自实现的功能
-
init
初始化函数,并创建一所学校 -
invoke
调用合约内部的函数 -
query
查询相关的信息 -
updateDiploma
由学校更新学生学历信息,并签名(返回记录信息) invoke -
enrollStudent
学校招生(返回学校信息) invoke -
createSchool
添加一名新学校 init -
createStudent
添加一名新学生 init -
getStudentByAddress
通过学生的地址访问学生的学历信息 query -
getRecordById
通过Id获取记录 query -
getRecords
获取全部记录(如果记录数大于10,返回前10个) query -
getSchoolByAddress
通过地址获取学校的信息 -
getBackgroundById
通过地点获取所存储的学历信息 -
writeRecord
写入记录 -
writeSchool
写入新创建的学校 -
writeStudent
写入新创建的学生
接口设计
createSchool
request参数:
args[0] 学校名称
args[1] 学校所在位置
response参数:
学校信息的json表示,当创建一所新学校时,该学校学生账户地址列表为空
createStudent
request参数:
args[0] 学生的姓名
response参数:
学生信息的json表示,刚创建过往学历信息列表为空
updateDiploma
request参数
args[0] 学校账户地址
args[1] 学校签名
args[2] 待修改学生的账户地址
args[3] //对该学生的学历进行怎样的修改,0:正常毕业 1:退学
response参数
返回修改记录的json表示
enrollStudent
request参数:
args[0] 学校账户地址
args[1] 学校签名
args[2] 学生账户地址
response参数
返回修改记录的json表示
getStudentByAddress
request参数
args[0] address
response参数
学生信息的json表示
getRecordById
request参数
args[0] 修改记录的ID
response参数
修改记录的json表示
getRecords
response参数
获取修改记录数组(如果个数大于10,返回前10个)
getSchoolByAddress
request参数
args[0] address
response参数
学校信息的json表示
getBackgroundById
request参数
args[0] ID
response参数
学历信息的json表示
测试
社区能源共享
功能描述
本 合约 以纽约实验性的能源微电网为例,作为一个简单的案例进行实现。
“在总统大道的一边,五户家庭通过太阳能板发电;在街道的另一边的五户家庭可以购买对面家庭不需要的电力。而连接这项交易的就是区块链网络,几乎不需要人员参与就可以管理记录交易。”但是这个想法是非常有潜力的,能够代表未来社区管理能源系统。”
布鲁克林微电网开发商 LO3 创始人 Lawrence Orsini 说:
“我们正在这条街道上建立一个可再生电力市场,来测试人们对于购买彼此手中的电力是否感兴趣。如果你在很远的地方生产能源,运输途中会有很多损耗,你也得不到这电力价值。但是如果你就在街对面,你就能高效的利用能源。”
在某一块区域内存在一个能源微电网,每一户家庭可能为生产者也可能为消费者。部分家庭拥有太阳能电池板,太阳能电池板的剩余电量为可以售出的电力的值,为了简化,单位为1.需要电力的家庭可以向有足够余额的电力的家庭购买电力。
账户私钥应该由安装在本地的客户端生成,本例中为了简便,使用模拟私钥和公钥。每位用户的私钥为guid+“1”,公钥为guid+“2”。签名方式简化为私钥+"1"
数据结构设计
在该智能合约中暂时只有一种角色,为每一户家庭用户。
- 家庭用户
- 账户地址
- 剩余能量 //部分家庭没有太阳能电池板,值为0
- 账户余额(电子货币)
- 编号
- 状态 //0:不可购买, 1:可以购买
- 账户公钥
- 账户私钥
- 交易(一笔交易必须同时具有卖方和买方的公钥签名,方能承认这笔交易。公钥签名生成规则,公钥+待创建交易的ID号,在本交易类型中,只要买家有足够的货币,卖家自动会对交易进行签名)
- 购买方地址
- 销售方地址
- 电量销售量
- 电量交易金额
- 编号
- 交易时间
function及各自实现的功能
-
init
初始化操作 -
invoke
调用合约内部的函数 -
query
查询相关的信息 -
createUser
创建新用户,并加入到能源微网中 init -
buyByAddress
向某一位用户购买一定量的电力 invoke -
getTransactionById
通过id获取交易内容 query -
getTransactions
获取交易(如果交易数大于10,获取前10个) query -
getHomes
获取用户(如果用户数大于10,获取前10个) query -
getHomeByAddress
通过地址获取用户 query -
changeStatus
某一位用户修改自身的状态 invoke -
writeUser
将新用户写入到键值对中 -
writeTransaction
记录交易 接口设计createUser
request参数:
args[0] 剩余能量值
args[1] 剩余金额
response参数:
新建家庭用户的json表示
buyByAddress
request参数:
args[0] 卖家的账户地址
args[1] 买家签名
args[2] 买家的账户地址
args[3] 想要购买的电量数值
response参数:
购买成功的话返回该transaction的json串。
购买失败返回error
getTransactionById
request参数:
args[0] 交易编号
response参数:
查询结果的transaction 交易表示
getTransactions
request参数:
none
response参数:
获取所有的交易列表(如果交易大于10,则返回前10个)
getHomeByAddress
request参数
args[0] address
response参数
用户信息的json表示
getHomes
response参数
获取所有的用户列表(如果用户个数大于10,则返回前10个)
changeStatus
request参数:
args[0] 账户地址
args[1] 账户签名
args[2] 对自己的账户进行的操作,0:设置为不可购买 1:设置状态为可购买
response参数:
修改后的用户信息json表示
测试
物流供应链简单案例
功能描述
该 智能合约 实现了一个简单的供应链应用案例,针对物流行业的应用场景。由于将合约的协议公开,并且签收快递时需要签名,可以在很大程度上保证不被冒领,实现了一手交钱,一手交货,同时提高了效率,确保了透明。
该智能合约中三种角色如下:
- 物流公司(本案例中只有1位)
- 寄货方(本案例中有多位)
- 收货方(本案例中有多位)
业务流程如下:
1、寄货方填写寄货单,物流公司根据寄货单寄快递。
2、寄快递过程中物流公司各个快递点对快递进行扫描,描述目前快递进度,并更新货单状态。寄货方和收货方可以根据单号进行查询。
3、快递到达后,收货方检查商品,确认无误后,扫码并使用私钥签名,支付相关费用,更新订单状态。
在实际中,物流费的支付分为两类:
- 1、寄货方支付。收货方签收快递后先预付给物流公司。
- 2、收货方支付。收货方签收快递后支付给物流公司。
在本案例中暂不考虑货物损坏、收货方失联、货物保值等的相关问题。具体实现逻辑如下:
- 创建账户。为每个用户生成唯一的私钥与地址。
- 生成寄货单。寄货方填写纸质寄货单,物流公司根据此生成电子单。
- 更新寄货单。物流公司旗下快递点根据配送信息更新电子寄货单。
- 收货方签收确认。收货方收到货物后,使用自己的私钥进行签收,完成相应的付款。
账户私钥应该由安装在本地的客户端生成,本例中为了简便,使用模拟私钥和公钥。每位用户的私钥为guid+“1”,公钥为guid+“2”。用户签名为私钥+“1”
数据结构设计
- 寄货单
- 寄货单编号
- 寄货方地址
- 收货方地址
- 寄货方联系方式
- 收货方联系方式
- 物流费用
- 物流费用支付类型 //0:寄货方支付 1:收货方支付
- 寄货方预支付费用 //模拟实际预支付,寄货方支付物流费下值为物流费,否则为0
- 快递配送信息 // 快递运送状态,所经过快递分拨中心与快递点的数组
- 收货方签名
- 寄货方
- 姓名
- 所在地址
- 账户地址
- 账户公钥
- 联系方式
- 账户余额
- 收货方
- 姓名
- 所在地址
- 账户地址
- 账户公钥
- 账户私钥
- 联系方式
- 账户余额
- 物流公司
- 账户公钥
- 账户私钥
- 名称
- 地址
- 联系方式
- 账户余额
- 物流公司旗下分拨中心与快递点
- 快递点
- 名称
- 所在地址
- 联系方式
- 快递点公钥
- 快递点私钥
- 快递点账户地址
function及各自实现的功能
-
init
初始化物流公司及其下相应快递点 -
invoke
调用合约内部的函数 -
query
查询相关的信息 -
createUser
创建用户 init -
createExpress
创建物流公司 init -
createExpressPoint
创建快递点 init -
createExpressOrder
寄货方创建寄货单 init -
finishExpressOrder
收货方签收寄货单 invoke -
addExpressPointer
物流公司添加新的快递点 invoke -
updateExpressOrder
更新物流公司订单,添加快递点的信息 invoke -
getExpressOrderById
查询订单状态 query -
getExpress
获取物流公司信息 query -
getUserByAddress
获取用户信息 query -
getExpressPointByAddress
获取快递点信息 query -
writeExpress
存储物流公司信息 (以物流公司账户地址进行存储) -
writeExpressOrder
存储寄货单 (以“express”+id 进行存储) -
writeUser
存储用户信息 (以地址进行存储) -
writeExpressPoint
存储物流点信息 (以快递点账户地址进行存储)
接口设计
createUser
request参数
args[0] 姓名
args[1] 所在地址
args[2] 联系方式
args[3] 账户余额
response参数
user信息的json表示
createExpressPointer
request参数
args[0] 姓名
args[1] 所在地址
args[2] 联系方式
response参数
物流点的信息的json表示
createExpress
request 参数
args[0] 名称
args[1] 地址
args[2] 联系方式
args[3] 账户余额
response 参数
物流公司信息的json表示
addExpressPointer
request参数
args[0] 添加快递点
response参数
物流公司信息的json表示
createExpressOrder
request参数
args[0] 寄货方地址
args[1] 收货方地址
args[2] 寄货方账户地址
args[3] 收货方账户地址
args[4] 寄货方联系方式
args[5] 收货方联系方式
args[6] 物流费用支付类型
args[7] 寄货方预支付费用 (收货方支付的话值为0)
args[8] 物流费用
response 参数
订单信息的json表示
updateExpressOrder
request参数
args[0] 订单id
args[1] 快递点地址
response参数
订单信息的json表示
finishExpressOrder
request参数
args[0] 收货方账户地址
args[1] 账户订单编号
args[2] 收货方签名
response参数
订单信息的json表示
getExpressOrderById
request参数:
args[0] id
response参数:
快递订单的json表示
getExpress
response参数:
快递信息的json表示
getUserByAddress
request参数
args[0] address
response参数
用户信息的json表示
getExpressPointerByAddress
request参数
args[0] address
response参数
快递点的json信息表示
测试
- HDU 1012 u Calculate e【暴力打表,水】
- Gym 100952C&&2015 HIAST Collegiate Programming Contest C. Palindrome Again !!【字符串,模拟】
- HDU 1013 Digital Roots【字符串,水】
- Gym 100952I&&2015 HIAST Collegiate Programming Contest I. Mancala【模拟】
- bootstrap + requireJS+ director+ knockout + web API = 一个时髦的单页程序
- Gym 100952E&&2015 HIAST Collegiate Programming Contest E. Arrange Teams【DFS+剪枝】
- Gym 100952H&&2015 HIAST Collegiate Programming Contest H. Special Palindrome【dp预处理+矩阵快速幂/打表解法】
- Gym 100952G&&2015 HIAST Collegiate Programming Contest G. The jar of divisors【简单博弈】
- Gym 100952F&&2015 HIAST Collegiate Programming Contest F. Contestants Ranking【BFS+STL乱搞(map+vector)+
- Gym 100952D&&2015 HIAST Collegiate Programming Contest D. Time to go back【杨辉三角预处理,组合数,dp】
- Gym 100952B&&2015 HIAST Collegiate Programming Contest B. New Job【模拟】
- 51 Nod 1008 N的阶乘 mod P【Java大数乱搞】
- 【AlphaGo Zero 核心技术-深度强化学习教程代码实战06】给Agent添加记忆功能
- Gym 100952A&&2015 HIAST Collegiate Programming Contest A. Who is the winner?【字符串,暴力】
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法