Merge remote-tracking branch 'origin/c4w_preview' into zyf_preview

This commit is contained in:
brightestboy 2019-03-15 17:21:03 +08:00
commit 9473996b4b
8 changed files with 188 additions and 36 deletions

View File

@ -45,6 +45,7 @@ import rep.sc.contract.ActionResult
*
*/
object Sandbox {
val SplitChainCodeId = "_"
//日志前缀
val log_prefix = "Sandbox-"
//t:中含txid可以找到原始交易; r:执行结果; merkle:执行完worldstate的hash; err:执行异常
@ -75,7 +76,7 @@ object Sandbox {
* @return 链码id字符串
*/
def getChaincodeId(c: ChaincodeId): String={
c.chaincodeName + c.version
c.chaincodeName + SplitChainCodeId + c.version
}
/** 从部署合约的交易获得其部署的合约的链码id
* @param t 交易对象
@ -95,7 +96,7 @@ object Sandbox {
* @constructor 以合约在区块链上的链码id作为合约容器id建立实例
* @param cid 链码id
*/
abstract class Sandbox(cid:String) extends Actor {
abstract class Sandbox(cid:ChaincodeId) extends Actor {
import TransProcessor._
import Sandbox._
import spray.json._
@ -105,8 +106,8 @@ abstract class Sandbox(cid:String) extends Actor {
val sTag =pe.getSysTag
//与底层交互的api实例
val shim = new Shim(context.system, cid)
//与底层交互的api实例,不同版本的合约KV空间重叠
val shim = new Shim(context.system, cid.chaincodeName)
val addr_self = akka.serialization.Serialization.serializedActorPath(self)
/** 消息处理主流程,包括对交易处理请求、交易的预执行处理请求、从存储恢复合约的请求

View File

@ -84,8 +84,9 @@ class Shim(system: ActorSystem, cName: String) {
import Shim._
import rep.storage.IdxPrefix._
val PRE_SPLIT = "_"
//本chaincode的 key前缀
val pre_key = WorldStateKeyPreFix + cName + "_"
val pre_key = WorldStateKeyPreFix + cName + PRE_SPLIT
//存储模块提供的system单例
val pe = PeerExtension(system)
//从交易传入, 内存中的worldState快照
@ -123,7 +124,7 @@ class Shim(system: ActorSystem, cName: String) {
}
def getStateEx(pre:String, key: Key): Array[Byte] = {
get(pre_key + key)
get(pre +PRE_SPLIT+ key)
}
//禁止脚本内调用此方法, 上下文应严密屏蔽不必要的方法和变量

View File

@ -151,9 +151,9 @@ class TransProcessor(name: String, da:String, parent: ActorRef) extends Actor {
*/
def getSandboxActor(t: Transaction,from:ActorRef, da:String): ActorRef = {
//如果已经有对应的actor实例复用之否则建实例,actor name加字母前缀
val cid = getTXCId(t)
val sn = PRE_SUB_ACTOR+cid
//log.info(s"idstr:$sn")
val tx_cid = getTXCId(t)
val cid = t.cid.get
val sn = PRE_SUB_ACTOR+tx_cid
//如果已经有对应的actor实例复用之否则建实例,actor name加字母前缀
val cref = context.child(sn)
cref match {
@ -164,7 +164,7 @@ class TransProcessor(name: String, da:String, parent: ActorRef) extends Actor {
//println(s"${pe.getSysTag} do invoke")
//尝试从持久化恢复,找到对应的Deploy交易并先执行之
val sr = ImpDataAccess.GetDataAccess(sTag)
val tidVal = sr.Get(WorldStateKeyPreFix+ cid)
val tidVal = sr.Get(WorldStateKeyPreFix+ tx_cid)
if(tidVal==null)
throw new SandboxException(ERR_INVOKE_CHAINCODE_NOT_EXIST)
val txId = SerializeUtils.deserialiseJson(tidVal).asInstanceOf[String]

View File

@ -65,7 +65,7 @@ object Compiler{
*/
class Compiler(targetDir: Option[File], bDebug:Boolean) {
//合约类名前缀
val PRE_CLS_NAME = "sha"
val PRE_CLS_NAME = "SC_"
//反射工具对象
val tb = currentMirror.mkToolBox()
//源文件路径

View File

@ -36,7 +36,7 @@ import rep.sc.contract.ActionResult
/**
* @author c4w
*/
class SandboxJS(cid:String) extends Sandbox(cid){
class SandboxJS(cid:ChaincodeId) extends Sandbox(cid){
val sandbox= new ScriptEngineManager().getEngineByName("nashorn")
sandbox.put("shim",shim)

View File

@ -36,9 +36,8 @@ import rep.sc.contract._
/**
* @author c4w
*/
class SandboxScala(cid:String) extends Sandbox(cid){
class SandboxScala(cid:ChaincodeId) extends Sandbox(cid){
var cobj:IContract = null
val pcid = cid
def doTransaction(t:Transaction,from:ActorRef, da:String):DoTransactionResult ={
//上下文可获得交易
@ -50,25 +49,39 @@ class SandboxScala(cid:String) extends Sandbox(cid){
val ctx = new ContractContext(shim,t)
//如果执行中出现异常,返回异常
try{
val cid = getTXCId(t)
val tx_cid = getTXCId(t)
val r: ActionResult = t.`type` match {
//如果cid对应的合约class不存在根据code生成并加载该class
case Transaction.Type.CHAINCODE_DEPLOY =>
//热加载code对应的class
val code = t.para.spec.get.codePackage
val clazz = Compiler.compilef(code,pcid)
cobj = clazz.getConstructor().newInstance().asInstanceOf[IContract]
//cobj = new ContractAssets()
cobj.init(ctx)
//deploy返回chancode.name
//利用kv记住cid对应的txid,并增加kv操作日志,以便恢复deploy时能根据cid找到当时deploy的tx及其代码内容
val txid = ByteString.copyFromUtf8(t.id).toByteArray()
val key = WorldStateKeyPreFix+ cid
shim.sr.Put(key,txid)
//ol value改为byte array
shim.ol.append(new Oper(key, null, txid))
new ActionResult(1,None)
//1.已存在同样的cid; 2.coder并非本人; 否决部署请求
val key_tx = WorldStateKeyPreFix+ tx_cid
val key_code = WorldStateKeyPreFix+ cid.chaincodeName
val coder = shim.sr.Get(key_code)
if(shim.sr.Get(key_tx) != null)
ActionResult(-1, Some("存在重复的合约Id"))
else if(coder!= null && !t.signature.get.certId.get.creditCode.equals(new String(coder))){
ActionResult(-2, Some("合约只能由部署者升级更新"))
}else{
//热加载code对应的class
val code = t.para.spec.get.codePackage
val clazz = Compiler.compilef(code,tx_cid)
cobj = clazz.getConstructor().newInstance().asInstanceOf[IContract]
//cobj = new ContractAssets()
cobj.init(ctx)
//deploy返回chancode.name
//利用kv记住cid对应的txid,并增加kv操作日志,以便恢复deploy时能根据cid找到当时deploy的tx及其代码内容
val txid = ByteString.copyFromUtf8(t.id).toByteArray()
shim.sr.Put(key_tx,txid)
shim.ol.append(new Oper(key_tx, null, txid))
//利用kv记住合约的开发者
val coder = ByteString.copyFromUtf8(t.signature.get.certId.get.creditCode).toByteArray()
shim.sr.Put(key_code,coder)
shim.ol.append(new Oper(key_code, null, coder))
new ActionResult(1,None)
}
//新建class实例并执行合约,传参为json数据
//TODO case Transaction.Type.CHAINCODE_DESC 增加对合约描述的处理
case Transaction.Type.CHAINCODE_INVOKE =>

View File

@ -58,16 +58,11 @@ case class Transfer(from:String, to:String, amount:Int)
* 根据action,找到对应的method并将传入的json字符串parse为method需要的传入参数
*/
def onAction(ctx: ContractContext,action:String, sdata:String ):ActionResult={
//println(s"onAction---")
//return "transfer ok"
val json = parse(sdata)
val json = parse(sdata)
action match {
case "transfer" =>
println(s"transfer oook")
transfer(ctx,json.extract[Transfer])
case "set" =>
println(s"set")
set(ctx, json.extract[Map[String,Int]])
}
}

View File

@ -0,0 +1,142 @@
/*
* Copyright 2018 Blockchain Technology and Application Joint Lab, Linkel Technology Co., Ltd, Beijing, Fintech Research Center of ISCAS.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BA SIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package rep.sc
import org.scalatest.{ BeforeAndAfterAll, FlatSpecLike, Matchers }
import akka.actor.ActorSystem
import akka.testkit.TestKit
import scala.concurrent.Await
import scala.concurrent.duration._
import rep.protos.peer._
import rep.sc.Sandbox._
import rep.app.system.ClusterSystem
import rep.app.system.ClusterSystem.InitType
import rep.network.PeerHelper
import org.json4s.{ DefaultFormats, jackson }
import de.heikoseeberger.akkahttpjson4s.Json4sSupport
import org.json4s._
import rep.network.module.ModuleManager
import rep.storage.ImpDataAccess
import rep.utils.Json4s._
import rep.utils.SerializeUtils.deserialise
import java.nio.ByteBuffer
import rep.sc.contract._
import java.io.IOException
import java.io.PrintWriter
import java.io.FileWriter
import java.io.File
import scala.collection.mutable.Map
import org.json4s.{DefaultFormats, Formats, jackson}
import org.json4s.native.Serialization.writePretty
import org.json4s.native.Serialization
import org.json4s.native.Serialization.{read, write}
import rep.crypto.cert.SignTool
/** 合约容器实现的单元测试
* @author c4w
* @param _system 测试用例所在的actor System.
*
*/
class DeploySpec(_system: ActorSystem)
extends TestKit(_system)
with Matchers
with FlatSpecLike
with BeforeAndAfterAll {
import rep.sc.TransProcessor.DoTransaction
import rep.sc.Sandbox.DoTransactionResult
import akka.testkit.TestProbe
import akka.testkit.TestActorRef
import Json4sSupport._
import rep.sc.tpl.SupplyType._
implicit val serialization = jackson.Serialization
// or native.Serialization
implicit val formats = DefaultFormats
def this() = this(ActorSystem("SandBoxSpec", new ClusterSystem("121000005l35120456.node1", InitType.MULTI_INIT, false).getConf))
override def afterAll: Unit = Await.ready(system.terminate(), Duration.Inf)
//Scala实现的资产管理合约测试包括合约的部署调用结果返回验证
"container" should "deploy asset contract and invoke it" in {
val sysName = "121000005l35120456.node1"
//建立PeerManager实例是为了调用transactionCreator(需要用到密钥签名)无他
val pm = system.actorOf(ModuleManager.props("pm", sysName))
//加载合约脚本
val s1 = scala.io.Source.fromFile("src/main/scala/rep/sc/tpl/ContractAssetsTPL.scala")
val l1 = try s1.mkString finally s1.close()
val fm: Map[String,Int] = Map("A" -> 100, "B"->100)
//准备探针以验证调用返回结果
val probe = TestProbe()
val db = ImpDataAccess.GetDataAccess(sysName)
var sandbox = system.actorOf(TransProcessor.props("sandbox", "", probe.ref))
//生成deploy交易
val cid = new ChaincodeId("Assets",1)
val t1 = PeerHelper.createTransaction4Deploy(sysName, cid,
l1, "",5000, rep.protos.peer.ChaincodeDeploy.CodeType.CODE_SCALA)
val msg_send1 = new DoTransaction(t1, probe.ref, "dbnumber")
probe.send(sandbox, msg_send1)
val msg_recv1 = probe.expectMsgType[Sandbox.DoTransactionResult](1000.seconds)
msg_recv1.r.code should be (1)
//同样合约id不允许重复部署
val msg_send2 = new DoTransaction(t1, probe.ref, "dbnumber")
probe.send(sandbox, msg_send1)
val msg_recv2 = probe.expectMsgType[Sandbox.DoTransactionResult](1000.seconds)
msg_recv2.r.code should be (-1)
val cid3 = new ChaincodeId("Assets",2)
//不同合约部署者不允许部署同样名称不同版本合约
val t3 = PeerHelper.createTransaction4Deploy(sysName, cid3,
l1, "",5000, rep.protos.peer.ChaincodeDeploy.CodeType.CODE_SCALA)
val msg_send3 = new DoTransaction(t3, probe.ref, "dbnumber")
probe.send(sandbox, msg_send3)
val msg_recv3 = probe.expectMsgType[Sandbox.DoTransactionResult](1000.seconds)
msg_recv3.r.code should be (1)
val cid4 = new ChaincodeId("Assets",3)
val sysName2 = "12110107bi45jh675g.node2"
//建立PeerManager实例是为了调用transactionCreator(需要用到密钥签名)无他
//val pm2 = system.actorOf(ModuleManager.props("pm2", sysName2))
//在这里添加一个私钥的装载因为系统默认只装载自己的节点私钥
SignTool.loadPrivateKey("12110107bi45jh675g.node2", "123", "jks/12110107bi45jh675g.node2.jks")
//同一合约部署者允许部署同样名称不同版本合约
val t4 = PeerHelper.createTransaction4Deploy(sysName2, cid4,
l1, "",5000, rep.protos.peer.ChaincodeDeploy.CodeType.CODE_SCALA)
val msg_send4 = new DoTransaction(t4, probe.ref, "dbnumber")
probe.send(sandbox, msg_send4)
val msg_recv4 = probe.expectMsgType[Sandbox.DoTransactionResult](1000.seconds)
msg_recv4.r.code should be (-2)
}
}