链码开发
智能合约
智能合约,作为受信任的分布式应用程序,从区块链中获得信任,在节点中达成基本共识,是区块链应用的业务逻辑。
有三个关键点适用于智能合约,尤其是应用于平台时:
多个智能合约在网络中同时运行,
它们可以动态部署(很多情况下任何人都可以部署),
应用代码应视为不被信任的,甚至可能是恶意的。
大多数现有的具有智能合约能力的区块链平台遵循 顺序执行 架构,其中共识协议:
验证并将交易排序,然后将它们传播到所有的节点,
每个节点按顺序执行交易。
几乎所有现有的区块链系统都可以找到顺序执行架构,从非许可平台,如 Ethereum (基于 PoW 共识)到许可平台,如 Tendermint 、Chain 和 Quorum 。
采用顺序执行架构的区块链执行智能合约的结果一定是确定的,否则,可能永远不会达成共识。为了解决非确定性问题,许多平台要求智能合约以非标准或特定领域的语言(例如 Solidity 编写,以便消除非确定性操作。这阻碍了平台的广泛采用,因为它要求开发人员学习新语言来编写智能合约,而且可能会编写错误的程序。
此外,由于所有节点都按顺序执行所有交易,性能和规模被限制。事实上系统要求智能合约代码要在每个节点上都执行,这就需要采取复杂措施来保护整个系统免受恶意合约的影响,以确保整个系统的弹性。
链码
智能合约,在 Fabric 中称之为“链码”。链码定义业务对象的不同状态,并管理对象在不同状态之间变化的过程。链码支持架构师和开发人员定义在区块链网络中协作的不同组织之间共享业务流程和数据。我们可以将链码视为交易的管理者,在链码中定义应用的方法,当应用程序请求某些操作时,调用链码中定义好的方法,链码中的代码会和Fabric状态数据库进行交互。
链码除了支持goLang,还支持Java VM和Node.js运行系统,所以JavaScript、TypeScript、Java或其他可以在运行系统上运行的语言。
在 Java 和 TypeScript 中,注释或装饰器用于提供有关链码及其结构的信息。 这提供了更丰富的开发体验——例如,可以强制执行作者信息或返回类型。 在 JavaScript 中,必须遵循约定,因此,可以自动确定的内容存在限制。
下文示例将同时使用Go、JavaScript和Java。
定义合约
Javascript先导入需要的类、标注,在定义智能合约是需要对Hyperledger Fabric Contract类进行扩展
const { Contract, Context } = require('fabric-contract-api');
class VoteContract extends Contract {...}
需注意JavaScript 类构造函数如何使用其超类通过一个命名空间来初始化自身:
constructor() {
super('org.papernet.commercialpaper');
}
Java定义智能合约类需要使用@Contract() 进行标注包装,@Default() 标注表明该智能合约是默认合约类。
@Contract(...)
@Default
public class VoteContract implements ContractInterface {...}
Go 先导入类”github.com/hyperledger/fabric-contract-api-go/contractapi”
type SmartContract struct {
contractapi.Contract
}
定义交易
我们可以定义一些函数,来实现我们具体的业务,在此我们定义了一个创建投票的交易,其中Vote的结构以及发布投票函数详细内容参见我们提供的代码;我们也定义了进行投票、查看投票结果、关闭投票等交易。
Javascript
async issue(ctx, description, optionlist, voteDiscri, peopleNum, pickUserList, banUserList)
Java:标注 @Transaction 用于标记该方法为交易定义;
@Transactionpublic CommercialPaper issue(TransactionContext ctx,
String description,
String[] optionlist,
String votediscri,
int peoplenum,
String[] pickuserlist,
String[] banuserlist,
) {...}
Golang:
func (s *SmartContract) CreateVote(
ctx contractapi.TransactionContextInterface,
description string,
optionlist []string,
votediscri string,
peoplenum int,
pickuserlist []string,
banuserlist []string )
error {}
访问账本
帐本中的每个状态数据都需要以下两个基本要素:
键(Key): 键 由 createCompositeKey() 使用固定名称和 state 密钥形成。在构造 Vote对象时分配了名称,state.getSplitKey() 确定每个状态的唯一键。
数据(Data): 数据 只是商业票据状态的序列化形式,使用 State.serialize() 方法创建。State 类使用 JSON 对数据进行序列化和反序列化,并根据需要使用 State 的业务对象类。
可以在我们提供的投票代码中看到如何将投票数据写到账本中,以createvote函数为例,先对vote信息进行序列化,
JavaScript:
votejson= json.Marshal(vote)
ctx.GetStub().PutState(strconv.Itoa(voteinfo.Votenum), votejson)
接下来我们会先描述如何把链码部署到联盟链上,最后我们会通过 示例 完整的将此投票链码实现。