DAY02视频

概念补充

hyperleger fabric的三个重要角色

  • client
    客户端,用来发起transaction process(提案),可以是cli、node sdk或者Java sdk
  • peers
    最常见的节点,维护了ledger的副本,记录验证同步数据
  • orderer
    接收背书后的请求,排序生成区块,最后交给peer节点

共识的达成

fabric的共识达成通过三个步骤

  1. 客户端发起提案,每个 peer 节点模拟执行,进行背书
  2. orderer 节点进行排序
  3. orderer 节点验证后生成区块交给 peer 节点去 apply
    三个步骤保证了区块链数据的一致性和正确性

    transaction流程

    1、sdk发起transaction proposal给peers进行背书(根据背书策略背书)
    2、背书节点模拟执行,返回签名的执行结果读写集(RW sets)交还给cli,sdk根据背书策略确定请求是否合法
    3、cli收集到读写集后给orderer节点进行线性排序
    4、orderer验证、排序、生成区块,再给所有peers进行ledger数据更新
    5、peers会验证orderer的读写集与当前世界状态是否一致来更新ledger,世界状态也随之变化
    6、最后sdk收到peers世界状态改变后的返还信息确认账本更新完成

    orderer节点

    解决双花问题(把并行事件线性排序)
    hyperledger的orderer类似中心机构出票的机制,所以效率很高没有挖矿的概念(为什么不挖矿1#14:00)(orderer节点防挂1#16:50)

    排序机制

    solo

    单一 orderer 节点用的玩具级别的排序服务,单一 orderer 服务器.采用 solo 方式

    kafka

    卡夫卡是阿帕奇的开源流式消息处理服务平台.提供非拜占庭错误(故障错误)的容错性即挂掉的排序节点不会返回误导性数据

    SBFT

    简单拜占庭容错,容忍集群中的 orderer 节点有不超过1/3的错误

    channels

    通道相当于hyperledger的子网络,不同channels相互独立,可拥有不同的sdk/ledger/peers
    orderer可以看到所有channel的数据

    state db世界状态数据库

    世界状态被存储在状态数据库里面。chaincode执行后stub.putState(key, Bufer.from(value))这些信息都是被以 key,value 的形式存放到状态数据库中通过 stub.getState(key) 的方式读出来。
    hyperledger fabric 支持两种模式的状态数据库:
  • levelDB 文件形式存储,不易查看管理.
  • couchDB 支持福查询,独立的容器数据库

    智能合约

    补充chaincode就是商业逻辑,任何更新账本的操作都智能通过智能合约来完成

    MSP(管理服务提供商)

    使用CA来颁发证书进行认证,默认fabric-ca api

实战(nodejs)

定义资产结构json

以下伪代码变量名直接使用中文

1
2
3
4
5
6
7
8
9
10
11
12
var 一次成绩 = {
学生姓名: " ",
学生学号uuid: " ",
考试科目: " ",
考试号uuid:" ",
信息修改者(即阅卷人):" ",
阅卷者uuid:"",
卷面总成绩: "0",
卷面详细成绩1:"0",
卷面详细成绩2:"0",
卷面详细成绩3:"0"
}

之类

创建基本智能合约框架

1
2
3
4
5
6
7
8
9
10
11
'use strict';
const shim = require (' fabric-shim ');//nodejs依赖
const util = require ('util');
const Chaincode = class{
//初始化智能合约的方法
async Init (stub) {
console.info ('Instantiated chaincode');
return shim.success ();
}
shim.start( new Chaincode ());
}

invoke(调用)函数

即如何调用函数的异常处理机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
async Invoke (stub){
let ret = stub.getFunctionAndParameters();//获取函数和参数
console.info( ret );
let method = this [ ret.fcn ];
if (! method ){
console.error('找不到要调用的函数,函数名:’+ ret.fcn);
throw new Error('找不到要调用的函数,函数名:’+ ret.fcn);
}
fry {
let payload = await method ( stub ,ret.params );//直接调用函数,获取返回值
return shim.success (payload );
} catch (err){
console . log(err);
return shim.error(err);
}
}

具体业务函数

如:查找

1
2
3
4
5
6
7
8
9
10
11
12
13
async query(stub,args){
if (args.length!=1){
throw new Error('错误调用参数');
}
let number = args[0];

let asBytes = await stub.getState(number);
if (!asBytes || asBytes.toString().length<=0){
throw new Error(asBytes + '不存在');
}
console.log(asBytes.toString());
return asBytes;
}

初始化账本initledger(单独写具体的init)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
async initLedger(stub,args){
console.info('开始初始化账本');
let grdes=[];
grades.push({
学生姓名: "A",
考试科目: "123",
卷面总成绩: "0"
});
grades.push({
学生姓名: "B",
考试科目: "456",
卷面总成绩: "0"
});

for (let i = 0;i < grdes.lenth;i++){
await stub.putState('GRADE' + i,Buffer.from(JSON.stringify(grades[i])));
console.info('添加',grades[i]);
}
console.info('结束初始化');
}

记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
async recordGrade(stub,args){
console.info('开始记录');
if (args.length !=4){
throw new Error('需要四个参数:ID,学生姓名,考试科目,卷面总成绩‘);
}
var grade = {
学生姓名: args[1],
考试科目: args[2],
卷面总成绩: args[3]
};

await stub.putState(args[0],Buffer.from(JSON.stringify(grade)));
console.info('记录成功');
}

查看所有:
修改:

---------------THEEND---------------