mirror of
https://gitee.com/BTAJL/repchain.git
synced 2024-12-04 12:48:32 +08:00
Merge remote-tracking branch 'origin/c4w_preview' into zyf_preview
This commit is contained in:
commit
9473996b4b
@ -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)
|
||||
|
||||
/** 消息处理主流程,包括对交易处理请求、交易的预执行处理请求、从存储恢复合约的请求
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
//禁止脚本内调用此方法, 上下文应严密屏蔽不必要的方法和变量
|
||||
|
@ -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]
|
||||
|
@ -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()
|
||||
//源文件路径
|
||||
|
@ -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)
|
||||
|
@ -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 =>
|
||||
|
@ -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]])
|
||||
}
|
||||
}
|
||||
|
142
src/test/scala/rep/sc/DeploySpec.scala
Normal file
142
src/test/scala/rep/sc/DeploySpec.scala
Normal 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)
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user