《HyperLedger Fabric 实战》—— 十、项目演练 – 反欺诈系统
注意点
1、用户唯一ID应该是姓名与身份证号组合后的MD5,这样可以防止加盟用户仅通过机器生成的大量身份证号来恶意刷区块链平台;
2、合约不应以用户为核心,而应以用户及小贷公司的合同作为合约核心,以防止加盟方自身的Bug等问题,向联盟平台写入大量无法溯源的用户数据,而导致错误;
编写贷款合约
finance/bean/compact.go
package bean
import (
"github.com/hyperledger/fabric/core/chaincode/shim"
"fmt"
"time"
"encoding/json"
)
// 合同全集详情
// 本条记录主键key由成员ID和合同ID联合组成,且备唯一性
type Compact struct {
Timestamp int64 `json:"timestamp"` // 本条记录创建时间戳
Uid string `json:"uid"` // 用户唯一ID(32位MD5值)
LoanAmount string `json:"loanAmount"` // 用户贷款金额
ApplyDate string `json:"applyDate"` // 申请日期
CompactStartDate string `json:"compactStartDate"` // 贷款开始日期
CompactEndDate string `json:"compactEndDate"` // 贷款计划终止日期
RealEndDate string `json:"realEndDate"` // 贷款实际终止日期
}
// 贷款操作
// args: UID、贷款金额、申请日期、贷款开始日期、贷款计划终止日期、合同ID
// name: 成员名称
func Loan(stub shim.ChaincodeStubInterface, args []string, name string) error {
if len(args) != 6 {
return fmt.Errorf("Parameter count error while Loan, count must 5")
}
if len(args[0]) != 32 {
return fmt.Errorf("Parameter uid length error while Loan, 32 is right")
}
if len(args[2]) != 14 {
return fmt.Errorf("Parameter ApplyDate length error while Loan, 14 is right")
}
if len(args[3]) != 14 {
return fmt.Errorf("Parameter CompactStartDate lengh error while Loan, 14 is right")
}
if len(args[4]) != 24 {
return fmt.Errorf("Parameter CompactEndDate length error while Loan, 14 is right")
}
var compact Compact
compact.Uid = args[0]
compact.LoanAmount = args[1]
compact.ApplyDate = args[2]
compact.CompactStartDate = args[3]
compact.CompactEndDate = args[4]
compact.Timestamp = time.Now().Unix()
compactJsonBytes, err := json.Marshal(&compact) // Json序列化
if err != nil {
return fmt.Errorf("Json serialize Compact fail while Loan, compact id = " + args[5])
}
// 生成合同联合主键
key, err := stub.CreateCompositeKey("Compact", []string{name, args[5]})
if err != nil {
return fmt.Errorf("Failed to CreateCompositeKey while Loan")
}
// 保存合同信息
err = stub.PutState(key, compactJsonBytes)
if err != nil {
return fmt.Errorf("Failed to PutState while Loan, compact id = " + args[5])
}
return nil
}
finance/utils/utils.go
package utils
import (
"github.com/hyperledger/fabric/core/chaincode/shim"
"strings"
"bytes"
"fmt"
"encoding/pem"
"crypto/x509"
)
// 获取当前操作智能合约成员具体名称,如a1aw28
func GetCreatorName(stub shim.ChaincodeStubInterface) (string, error) {
name, err := GetCreator(stub) // 获取当前智能合约操作成员名称
if err != nil {
return "", err
}
// 格式化当前智能合约操作成员名称
memberName := name[(strings.Index(name, "@") + 1):strings.LastIndex(name, ".example.com")]
return memberName, nil;
}
// 获取操作成员
func GetCreator(stub shim.ChaincodeStubInterface) (string, error) {
creatorByte, _ := stub.GetCreator()
certStart := bytes.IndexAny(creatorByte, "-----BEGIN")
if certStart == -1 {
fmt.Errorf("No certificate found")
}
certText := creatorByte[certStart:]
bl, _ := pem.Decode(certText)
if bl == nil {
fmt.Errorf("Could not decode the PEM structure")
}
cert, err := x509.ParseCertificate(bl.Bytes)
if err != nil {
fmt.Errorf("ParseCertificate failed")
}
uname := cert.Subject.CommonName
return uname, nil
}
finance/finance.go
package main
import (
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/protos/peer"
"fmt"
"github.com/hyperledger/fabric/examples/chaincode/go/finance/bean"
"github.com/hyperledger/fabric/examples/chaincode/go/finance/utils"
)
type Finance struct {
}
func (t *Finance) Init(stub shim.ChaincodeStubInterface) peer.Response {
args := stub.GetStringArgs()
if len(args) != 0 {
return shim.Error("Parameter error while Init")
}
return shim.Success(nil)
}
func (t *Finance) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
fn, args := stub.GetFunctionAndParameters()
switch fn {
case "loan": // 记录贷款数据
return loan(stub, args)
default:
return shim.Error("Unknown func type while Invoke, please check")
}
}
// 记录贷款数据
func loan(stub shim.ChaincodeStubInterface, args []string) peer.Response {
name, err := utils.GetCreatorName(stub)
if err != nil {
return shim.Error(err.Error())
}
err = bean.Loan(stub, args, name)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success([]byte("记录贷款数据成功"))
}
func main() {
if err := shim.Start(new(Finance)); err != nil {
fmt.Printf("Chaincode startup error: %s", err)
}
}
上传合约
上传整个 finance 包到 peer 节点服务器的 aberic/chaincode/go 目录下。
安装 & 实例化
peer chaincode install -n finance -p github.com/hyperledger/fabric/aberic/chaincode/go/finance -v 1.0
peer chaincode instantiate -o orderer.example.com:7050 -C mychannel -n finance -c '{"Args":[]}' -P "OR ('Org1MSP.member', 'Org2MSP.member')" -v 1.0
升级合约
这一步非必须,笔者是因为第一版合约开发有问题,因此需要升级。
升级需先安装
peer chaincode install -n finance -p github.com/hyperledger/fabric/aberic/chaincode/go/finance -v 1.1
peer chaincode upgrade -C mychannel -n finance -p github.com/hyperledger/fabric/aberic/chaincode/go/finance -v 1.1 -c '{"Args":[]}'
执行贷款操作
peer chaincode invoke -C mychannel -n finance -v 1.0 -c '{"Args":["loan", "4e4d6c332b6fe62a63afe56171fd3725", "200000", "20180420080812", "20180421080812", "20190421080812", "000001"]}'
peer chaincode invoke -C mychannel -n finance -v 1.0 -c '{"Args":["loan", "4e4d6c332b6fe62a63afe56171fd3725", "200000", "20180420080812", "20180421080812", "20190421080812", "000002"]}'
peer chaincode invoke -C mychannel -n finance -v 1.0 -c '{"Args":["loan", "4e4d6c332b6fe62a63afe56171fd3725", "200000", "20180420080812", "20180421080812", "20190421080812", "000003"]}'
CouchDB 索引 & 查询
浏览器打开地址:http://192.168.24.251:5984/_utils/#/database/mychannel_finance/_index
{
"index": {
"fields": [
"uid"
]
},
"name": "indexUid",
"ddoc": "indexUidDoc",
"type": "json"
}
CouchDB -> Run A Query with Mango ->
{
"selector": {
"uid": "4e4d6c332b6fe62a63afe56171fd3725"
}
}
Post 方式查询
Postman 发送上面查询 json 到 http://192.168.24.251:5984/mychannel_finance/_find
。
案例强调
本案例只是演练需求落地,但检索方面不能以这样的方式上生产环境,必须为所有加盟方提供开源可见的查询方法,并在智能合约中体现。