学历认证
功能描述
该 智能合约 实现了一个简单的征信管理的案例。针对于学历认证领域,由于条约公开,在条约外无法随意篡改的特性,天然具备稳定性和中立性。
/*
authors:
"swb"<swbsin@163.com>
"Gymgle"<ymgongcn@gmail.com>
MIT License
*/
package main
import (
"crypto/md5"
"crypto/rand"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"strconv"
"time"
"github.com/hyperledger/fabric/core/chaincode/shim"
pb "github.com/hyperledger/fabric/protos/peer"
)
type SimpleChaincode struct {
}
var BackGroundNo int = 0
var RecordNo int = 0
type School struct {
Name string
Location string
Address string
PriKey string
PubKey string
StudentAddress []string
}
type Student struct {
Name string
Address string
BackgroundId []int
}
// 学历信息,当离开学校才能记入
type Background struct {
Id int
ExitTime int64
Status string //0:毕业 1:退学
}
type Record struct {
Id int
SchoolAddress string
StudentAddress string
SchoolSign string
ModifyTime int64
ModifyOperation string // 0:正常毕业 1:退学 2:入学
}
/*
* 区块链网络实例化"diploma"智能合约时调用该方法
*/
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
return shim.Success(nil)
}
/*
* 客户端发起请求执行"diploma"智能合约时会调用 Invoke 方法
*/
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
// 获取请求调用智能合约的方法和参数
function, args := stub.GetFunctionAndParameters()
// Route to the appropriate handler function to interact with the ledger appropriately
if function == "createSchool" {
return t.createSchool(stub, args)
} else if function == "createStudent" {
return t.createStudent(stub, args)
} else if function == "enrollStudent" {
return t.enrollStudent(stub, args)
} else if function == "updateDiploma" {
return t.updateDiploma(stub, args)
} else if function == "getRecords" {
return t.getRecords(stub)
} else if function == "getRecordById" {
return t.getRecordById(stub, args)
} else if function == "getStudentByAddress" {
return t.getStudentByAddress(stub, args)
} else if function == "getSchoolByAddress" {
return t.getSchoolByAddress(stub, args)
} else if function == "getBackgroundById" {
return t.getBackgroundById(stub, args)
}
return shim.Success(nil)
}
/*
* 添加一所新学校
* args[0] 学校名称
* args[1] 学校所在位置
*/
func (t *SimpleChaincode) createSchool(stub shim.ChaincodeStubInterface, args []string) pb.Response {
if len(args) != 2 {
return shim.Error("Incorrect number of arguments. Expecting 2")
}
var school School
var schoolBytes []byte
var stuAddress []string
var address, priKey, pubKey string
address, priKey, pubKey = GetAddress()
school = School{Name: args[0], Location: args[1], Address: address, PriKey: priKey, PubKey: pubKey, StudentAddress: stuAddress}
err := writeSchool(stub, school)
if err != nil {
shim.Error("Error write school")
}
schoolBytes, err = json.Marshal(&school)
if err != nil {
return shim.Error("Error retrieving schoolBytes")
}
return shim.Success(schoolBytes)
}
/*
* 添加一名新学生
* args[0] 学生的姓名
*/
func (t *SimpleChaincode) createStudent(stub shim.ChaincodeStubInterface, args []string) pb.Response {
if len(args) != 1 {
return shim.Error("Incorrect number of arguments. Expecting 1")
}
var student Student
var studentBytes []byte
var stuAddress string
var bgID []int
stuAddress, _, _ = GetAddress()
student = Student{Name: args[0], Address: stuAddress, BackgroundId: bgID}
err := writeStudent(stub, student)
if err != nil {
return shim.Error("Error write student")
}
studentBytes, err = json.Marshal(&student)
if err != nil {
return shim.Error("Error retrieving studentBytes")
}
return shim.Success(studentBytes)
}
/*
* 学校招生(返回学校信息)
* args[0] 学校账户地址
* args[1] 学校签名
* args[2] 学生账户地址
*/
func (t *SimpleChaincode) enrollStudent(stub shim.ChaincodeStubInterface, args []string) pb.Response {
if len(args) != 3 {
return shim.Error("Incorrect number of arguments. Expecting 3")
}
schAddress := args[0]
schoolSign := args[1]
stuAddress := args[2]
var school School
var schBytes []byte
var err error
// 根据学校账户地址获取学校信息
schBytes, err = stub.GetState(schAddress)
if err != nil {
return shim.Error("Error retrieving data")
}
err = json.Unmarshal(schBytes, &school)
if err != nil {
return shim.Error("Error unmarshalling data")
}
var record Record
record = Record{Id: RecordNo, SchoolAddress: schAddress, StudentAddress: stuAddress, SchoolSign: schoolSign, ModifyTime: time.Now().Unix(), ModifyOperation: "2"} // 2 表示入学
err = writeRecord(stub, record)
if err != nil {
return shim.Error("Error write record")
}
school.StudentAddress = append(school.StudentAddress, stuAddress)
err = writeSchool(stub, school)
if err != nil {
return shim.Error("Error write school")
}
RecordNo = RecordNo + 1
recordBytes, err := json.Marshal(&record)
if err != nil {
return shim.Error("Error retrieving recordBytes")
}
return shim.Success(recordBytes)
}
/*
* 由学校更新学生学历信息,并签名(返回记录信息)
* args[0] 学校账户地址
* args[1] 学校签名
* args[2] 待修改学生的账户地址
* args[3] 对该学生的学历进行怎样的修改,0:正常毕业 1:退学
*/
func (t *SimpleChaincode) updateDiploma(stub shim.ChaincodeStubInterface, args []string) pb.Response {
if len(args) != 4 {
return shim.Error("Incorrect number of arguments. Expecting 4")
}
schAddress := args[0]
schoolSign := args[1]
stuAddress := args[2]
modOperation := args[3]
var recordBytes []byte
var student Student
var stuBytes []byte
var err error
// 根据学生账户地址获取学生信息
stuBytes, err = stub.GetState(stuAddress)
if err != nil {
return shim.Error("Error retrieving data")
}
err = json.Unmarshal(stuBytes, &student)
if err != nil {
return shim.Error("Error unmarshalling data")
}
var record Record
record = Record{Id: RecordNo, SchoolAddress: schAddress, StudentAddress: stuAddress, SchoolSign: schoolSign, ModifyTime: time.Now().Unix(), ModifyOperation: modOperation}
err = writeRecord(stub, record)
if err != nil {
return shim.Error("Error write record")
}
var background Background
background = Background{Id: BackGroundNo, ExitTime: time.Now().Unix(), Status: modOperation}
err = writeBackground(stub, background)
if err != nil {
return shim.Error("Error write background")
}
// 如果学生正常毕业,也要更新学生的教育背景
if modOperation == "0" {
student.BackgroundId = append(student.BackgroundId, BackGroundNo)
student = Student{Name: student.Name, Address: student.Address, BackgroundId: student.BackgroundId}
err = writeStudent(stub, student)
if err != nil {
return shim.Error("Error write student")
}
}
BackGroundNo = BackGroundNo + 1
recordBytes, err = json.Marshal(&record)
if err != nil {
return shim.Error("Error retrieving schoolBytes")
}
return shim.Success(recordBytes)
}
/*
* 通过学生的地址获取学生的学历信息
* args[0] address
*/
func (t *SimpleChaincode) getStudentByAddress(stub shim.ChaincodeStubInterface, args []string) pb.Response {
if len(args) != 1 {
return shim.Error("Incorrect number of arguments. Expecting 1")
}
stuBytes, err := stub.GetState(args[0])
if err != nil {
shim.Error("Error retrieving data")
}
return shim.Success(stuBytes)
}
/*
* 通过地址获取学校的信息
* args[0] address
*/
func (t *SimpleChaincode) getSchoolByAddress(stub shim.ChaincodeStubInterface, args []string) pb.Response {
if len(args) != 1 {
return shim.Error("Incorrect number of arguments. Expecting 1")
}
schBytes, err := stub.GetState(args[0])
if err != nil {
shim.Error("Error retrieving data")
}
return shim.Success(schBytes)
}
/*
* 通过 Id 获取记录
* args[0] 记录的 Id
*/
func (t *SimpleChaincode) getRecordById(stub shim.ChaincodeStubInterface, args []string) pb.Response {
if len(args) != 1 {
return shim.Error("Incorrect number of arguments. Expecting 1")
}
recBytes, err := stub.GetState("Record" + args[0])
if err != nil {
return shim.Error("Error retrieving data")
}
return shim.Success(recBytes)
}
/*
* 获取全部记录(如果记录数大于10,返回前10个)
*/
func (t *SimpleChaincode) getRecords(stub shim.ChaincodeStubInterface) pb.Response {
var records []Record
var number string
var err error
var record Record
var recBytes []byte
if RecordNo < 10 {
i := 0
for i <= RecordNo {
number = strconv.Itoa(i)
recBytes, err = stub.GetState("Record" + number)
if err != nil {
return shim.Error("Error get detail")
}
err = json.Unmarshal(recBytes, &record)
if err != nil {
return shim.Error("Error unmarshalling data")
}
records = append(records, record)
i = i + 1
}
} else {
i := 0
for i < 10 {
number = strconv.Itoa(i)
recBytes, err = stub.GetState("Record" + number)
if err != nil {
return shim.Error("Error get detail")
}
err = json.Unmarshal(recBytes, &record)
if err != nil {
return shim.Error("Error unmarshalling data")
}
records = append(records, record)
i = i + 1
}
}
recordsBytes, err := json.Marshal(&records)
if err != nil {
shim.Error("Error get records")
}
return shim.Success(recordsBytes)
}
/*
* 通过 Id 获取所存储的学历信息
* args[0] ID
*/
func (t *SimpleChaincode) getBackgroundById(stub shim.ChaincodeStubInterface, args []string) pb.Response {
backBytes, err := stub.GetState("BackGround" + args[0])
if err != nil {
return shim.Error("Error retrieving data")
}
return shim.Success(backBytes)
}
func writeRecord(stub shim.ChaincodeStubInterface, record Record) error {
var recID string
recordBytes, err := json.Marshal(&record)
if err != nil {
return err
}
recID = strconv.Itoa(record.Id)
err = stub.PutState("Record"+recID, recordBytes)
if err != nil {
return errors.New("PutState Error" + err.Error())
}
return nil
}
func writeSchool(stub shim.ChaincodeStubInterface, school School) error {
schBytes, err := json.Marshal(&school)
if err != nil {
return err
}
err = stub.PutState(school.Address, schBytes)
if err != nil {
return errors.New("PutState Error" + err.Error())
}
return nil
}
func writeStudent(stub shim.ChaincodeStubInterface, student Student) error {
stuBytes, err := json.Marshal(&student)
if err != nil {
return err
}
err = stub.PutState(student.Address, stuBytes)
if err != nil {
return errors.New("PutState Error" + err.Error())
}
return nil
}
func writeBackground(stub shim.ChaincodeStubInterface, background Background) error {
var backID string
backBytes, err := json.Marshal(&background)
if err != nil {
return err
}
backID = strconv.Itoa(background.Id)
err = stub.PutState("BackGround"+backID, backBytes)
if err != nil {
return errors.New("PutState Error" + err.Error())
}
return nil
}
/*
* 生成Address
*/
func GetAddress() (string, string, string) {
var address, priKey, pubKey string
b := make([]byte, 48)
if _, err := io.ReadFull(rand.Reader, b); err != nil {
return "", "", ""
}
h := md5.New()
h.Write([]byte(base64.URLEncoding.EncodeToString(b)))
address = hex.EncodeToString(h.Sum(nil))
priKey = address + "1"
pubKey = address + "2"
return address, priKey, pubKey
}
func main() {
err := shim.Start(new(SimpleChaincode))
if err != nil {
fmt.Printf("Error starting Simple chaincode: %s", err)
}
}
该智能合约中三种角色如下:
- 学校
- 个人
- 需要学历认证的机构或公司
学校可以根据相关信息在区块链上为某位个人授予学历,相关机构可以查询某人的学历信息,由于使用私钥签名,确保了信息的真实有效。
为了简单,尽量简化相关的业务,另未完成学业的学生因违纪或外出创业退学,学校可以修改其相应的学历信息。
账户私钥应该由安装在本地的客户端生成,本例中为了简便,使用模拟私钥和公钥。
数据结构设计
- 学校
- 名称
- 所在位置
- 账号地址
- 账号公钥
- 账户私钥
- 学校学生
- 个人
- 姓名
- 账号地址
- 过往学历
- 学历信息
- 学历信息编号
- 就读学校
- 就读年份
- 完成就读年份
- 就读状态 // 0:毕业 1:退学
- 修改记录(入学也相当于一种修改记录)
- 编号
- 学校账户地址(一般根据账户地址可以算出公钥地址,然后可以进行校验)
- 学校签名
- 个人账户地址
- 个人公钥地址(个人不需要公钥地址)
- 修改时间
- 修改操作// 0:正常毕业 1:退学 2:入学
对学历操作信息所有的操作都归为记录。
function及各自实现的功能
init
初始化函数invoke
调用合约内部的函数updateDiploma
由学校更新学生学历信息,并签名(返回记录信息)enrollStudent
学校招生(返回学校信息)createSchool
添加一名新学校createStudent
添加一名新学生getStudentByAddress
通过学生的账号地址访问学生的学历信息getRecordById
通过Id获取记录getRecords
获取全部记录(如果记录数大于 10,返回前 10 个)getSchoolByAddress
通过学校账号地址获取学校的信息getBackgroundById
通过学历 Id 获取所存储的学历信息writeRecord
写入记录writeSchool
写入新创建的学校writeStudent
写入新创建的学生
接口设计
createSchool
request参数:
args[0] 学校名称
args[1] 学校所在位置
response参数:
学校信息的字节数组,当创建一所新学校时,该学校学生账户地址列表为空
createStudent
request参数:
args[0] 学生的姓名
response参数:
学生信息的字节数组表示,刚创建过往学历信息列表为空
updateDiploma
request参数
args[0] 学校账户地址
args[1] 学校签名
args[2] 待修改学生的账户地址
args[3] //对该学生的学历进行怎样的修改,0:正常毕业 1:退学
response参数
返回修改记录的字节数组表示
enrollStudent
request参数:
args[0] 学校账户地址
args[1] 学校签名
args[2] 学生账户地址
response参数
返回修改记录的字节数组表示
getStudentByAddress
request参数
args[0] address
response参数
学生信息的字节数组表示
getRecordById
request参数
args[0] 修改记录的ID
response参数
修改记录的字节数组表示
getRecords
response参数
获取修改记录数组(如果个数大于10,返回前10个)
getSchoolByAddress
request参数
args[0] address
response参数
学校信息的字节数组表示
getBackgroundById
request参数
args[0] ID
response参数
学历信息的字节数组表示