5

简述如何实现区块链中的jvm

 3 years ago
source link: https://learnblockchain.cn/article/1988
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

虚拟机是区块链中的一个关键组件,用来执行智能合约,需要满足安全性和一致性,所谓的安全性一般是指合约代码需要在隔离的沙箱环境中运行,以免错误或恶意代码造成对区块链系统的损害。而一致性...

虚拟机是区块链中的一个关键组件,用来执行智能合约,需要满足安全性和一致性,所谓的安全性一般是指合约代码需要在隔离的沙箱环境中运行,以免错误或恶意代码造成对区块链系统的损害。而一致性是指区块链网络中任意诚实的节点执行同一个合约,如果输入参数一致,输出结果都应该是一致的。目前比较主流的虚拟机实现包括EVM、WASM,其他的实现还有如星云链的谷歌V8,Nervos的RISC-V,SOLANA的BPF等,设计如此众多不同虚拟机的目的,主要是对区块链系统互操作性、安全性、执行性能、硬件兼容性等因素的考量。如EVM更注重的是安全性,虽然Solidity设计是图灵完备的语言,但是通过自身限定的最小语法集,来保证合约的安全可控,而星云链采用V8,更多的一方面也是考虑智能合约的互操作性,使用javascript降低了合约的编写门槛。尽管Solidity也是类javascript语言,并且入门门槛也不是很高,但是对于很多想入行区块链的程序员来说,学习一门新的语言还是需要一些成本的。 JVM本身就是一种虚拟机,而且java语言又是最为流行的几大语言之一,为什么区块链中使用的较少呢?个人总结主要包含如下几个原因: 1、语言的可控性,同c++/golang/rust等主流开发语言类似,java也是系统级语言,可以访问操作系统的任意资源,例如读写文件、访问网络等 2、执行性能,java是解释性语言,jvm载入的class文件是一种中间语言,真正执行的时候才会进一步解释成机器码执行,所以主流的jvm都会引入JIT对热点代码进行及时编译来提高执行效率 3、语言的复杂性,java有很多复杂的语法,比如支持OOP的接口、多态、反射和多线程控制等,对于更注重过程化执行的智能合约用处不大,而且容易影响合约执行的一致性。 那如果能解决这些问题,针对jvm做一个自定义的最小集合裁剪,那对于很多java开发者来说,还是比较有吸引力的。 下面我们对如何实现一个区块链中的jvm方案做一个简单的描述,如果使用golang语言,建议参考下这个代码库: https://github.com/zxh0/jvm.go (笔者曾经参考这个代码实现过一个jvm虚拟机,也有很多问题没有很好的解决,这里抛砖引玉,和大家共同探讨)

  1. 如何传递参数 java是OOP的,如果编写合约,需要先定义一个合约类,合约类中定义的类方法就是合约能够执行的方法。区块链上合约执行都是通过交易触发的,而合约地址、输入参数需要通过交易携带,首先要解决的问题就是如何传递这些信息给合约,对于基本类型处理比较简单,可以都编码成字符串进行传递,合约执行的时候根据方法的参数类型解析成对应的结果,而对于自定义类的对象如何处理呢?可以使用json、xml、proto等编码方式对类对象进行逐字段编码,如果字段也是个自定义类对象,那就递归进行编码。这几种编码方式的有点是利于阅读,但是缺点就是空间占用较大,对于区块链这种冗余分布式存储机制来讲,占用空间越少越好,所以可以采用以太坊中使用的RLP(递归长度前缀)编码方式。 如何载入java系统类 参考jvm.go代码库,可以在目标节点上预安装一个jdk环境,然后在节点应用启动时把需要用到的java系统类包预加载入内存的一个map中。这里可以根据实际情况对导入进来的系统类包做一些选择裁剪,同时过滤掉容易造成执行结果不一致的包,比如随机数、多线程,IO操作等。 如何模拟离线沙盒执行环境 笔者采用的是如上裁剪系统类包的做法,限制了用户合约直接引用网络、读写、cmd等操作权限,同时不允许用户合约直接引用第三方jar包或native执行第三方库等,保证合约的安全性。这种方式比较简单粗暴并不是特别好的方案,对于恶意代码跳过系统包,自己实现类似的访问资源的操作,就无法检测出来,并不是一个好的方案,如果有好的方案可以一起讨论。 如何屏蔽随机数、多线程等容易造成执行结果不一致的语法执行 同上也是限制引入包的做法屏蔽,对于恶意代码自己实现随机数,这个也同样无法检测出来。 如何启动合约执行 jvm执行是需要找到Main入口,而合约代码可以不提供Main函数,我们可以在生成合约包的工具中自动生成相应的Main函数,添加new合约对象,然后调用对应方法的代码。 如何读写合约状态变量 java存取的指令码主要是load和store系列指令。但是对于合约启动执行时,需要先从数据库中读取到历史的状态,比如合约中的余额变量,如上自动生成的Main函数中会先new一个合约对象,于是我们可以捕获到合约对象的构造函数,注入读取数据库代码,并将合约对象的成员变量全部恢复。这里也可以参考solidity的方法,区分内存和存储变量,可以通过注解来实现。jvm可以解析变量的注解,如果注解是Storge,就存取数据库,否则从当前内存获取。 如何返回执行结果 用户合约代码可能会执行失败,比如余额不足,权限不匹配等,我们可以将交易回执的指针关联到合约执行的主Thread中,这样在执行失败的地方,都可以随时返回,并且自定义相关错误信息。 如何回退交易 可以参考以太坊的方案,利用MPT保存状态变量的历史值,状态变量的修改是实时修改本地存储,如果交易失败回滚,直接修改为上一个历史状态即可。 如何提升执行性能 我们可以用native实现类似以太坊的系统合约的功能或替换掉java中比较耗时的操作,比如hashmap。参考jvm.go的实现,我们可以定制很多native方法来提升执行效率,例如合约中执行sha256,如果用java自带的方法,会增加几千条指令。我们也可以利用native方法来完成自定义的一些功能,比如零知识证明、同态加密库等。 如何解决停机问题 参考以太坊的gas机制,按照指令粒度统计燃烧的gas,对于涉及到存储的指令,根据存储的数据大小增加gas比例。这样实现的好处是gas统计比较精确,但是java指令较多,代码实现繁琐,执行性能也会影响。另外看到业内使用WASM的主流方案都是统计指令注入到了编译生成的机器码中,这个方案也可以尝试,但需要自己实现javac。 以上是笔者对jvm实现方案的一些简单看法,如果对区块链jvm有兴趣的话欢迎一起交流。

本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

  • 发表于 14分钟前
  • 阅读 ( 5 )
  • 学分 ( 0 )
  • 分类:智能合约

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK