mirror of
https://gitee.com/BTAJL/repchain.git
synced 2024-12-05 05:08:29 +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 {
|
object Sandbox {
|
||||||
|
val SplitChainCodeId = "_"
|
||||||
//日志前缀
|
//日志前缀
|
||||||
val log_prefix = "Sandbox-"
|
val log_prefix = "Sandbox-"
|
||||||
//t:中含txid可以找到原始交易; r:执行结果; merkle:执行完worldstate的hash; err:执行异常
|
//t:中含txid可以找到原始交易; r:执行结果; merkle:执行完worldstate的hash; err:执行异常
|
||||||
@ -75,7 +76,7 @@ object Sandbox {
|
|||||||
* @return 链码id字符串
|
* @return 链码id字符串
|
||||||
*/
|
*/
|
||||||
def getChaincodeId(c: ChaincodeId): String={
|
def getChaincodeId(c: ChaincodeId): String={
|
||||||
c.chaincodeName + c.version
|
c.chaincodeName + SplitChainCodeId + c.version
|
||||||
}
|
}
|
||||||
/** 从部署合约的交易,获得其部署的合约的链码id
|
/** 从部署合约的交易,获得其部署的合约的链码id
|
||||||
* @param t 交易对象
|
* @param t 交易对象
|
||||||
@ -95,7 +96,7 @@ object Sandbox {
|
|||||||
* @constructor 以合约在区块链上的链码id作为合约容器id建立实例
|
* @constructor 以合约在区块链上的链码id作为合约容器id建立实例
|
||||||
* @param cid 链码id
|
* @param cid 链码id
|
||||||
*/
|
*/
|
||||||
abstract class Sandbox(cid:String) extends Actor {
|
abstract class Sandbox(cid:ChaincodeId) extends Actor {
|
||||||
import TransProcessor._
|
import TransProcessor._
|
||||||
import Sandbox._
|
import Sandbox._
|
||||||
import spray.json._
|
import spray.json._
|
||||||
@ -105,8 +106,8 @@ abstract class Sandbox(cid:String) extends Actor {
|
|||||||
val sTag =pe.getSysTag
|
val sTag =pe.getSysTag
|
||||||
|
|
||||||
|
|
||||||
//与底层交互的api实例
|
//与底层交互的api实例,不同版本的合约KV空间重叠
|
||||||
val shim = new Shim(context.system, cid)
|
val shim = new Shim(context.system, cid.chaincodeName)
|
||||||
val addr_self = akka.serialization.Serialization.serializedActorPath(self)
|
val addr_self = akka.serialization.Serialization.serializedActorPath(self)
|
||||||
|
|
||||||
/** 消息处理主流程,包括对交易处理请求、交易的预执行处理请求、从存储恢复合约的请求
|
/** 消息处理主流程,包括对交易处理请求、交易的预执行处理请求、从存储恢复合约的请求
|
||||||
|
@ -84,8 +84,9 @@ class Shim(system: ActorSystem, cName: String) {
|
|||||||
import Shim._
|
import Shim._
|
||||||
import rep.storage.IdxPrefix._
|
import rep.storage.IdxPrefix._
|
||||||
|
|
||||||
|
val PRE_SPLIT = "_"
|
||||||
//本chaincode的 key前缀
|
//本chaincode的 key前缀
|
||||||
val pre_key = WorldStateKeyPreFix + cName + "_"
|
val pre_key = WorldStateKeyPreFix + cName + PRE_SPLIT
|
||||||
//存储模块提供的system单例
|
//存储模块提供的system单例
|
||||||
val pe = PeerExtension(system)
|
val pe = PeerExtension(system)
|
||||||
//从交易传入, 内存中的worldState快照
|
//从交易传入, 内存中的worldState快照
|
||||||
@ -123,7 +124,7 @@ class Shim(system: ActorSystem, cName: String) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def getStateEx(pre:String, key: Key): Array[Byte] = {
|
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 = {
|
def getSandboxActor(t: Transaction,from:ActorRef, da:String): ActorRef = {
|
||||||
//如果已经有对应的actor实例,复用之,否则建实例,actor name加字母前缀
|
//如果已经有对应的actor实例,复用之,否则建实例,actor name加字母前缀
|
||||||
val cid = getTXCId(t)
|
val tx_cid = getTXCId(t)
|
||||||
val sn = PRE_SUB_ACTOR+cid
|
val cid = t.cid.get
|
||||||
//log.info(s"idstr:$sn")
|
val sn = PRE_SUB_ACTOR+tx_cid
|
||||||
//如果已经有对应的actor实例,复用之,否则建实例,actor name加字母前缀
|
//如果已经有对应的actor实例,复用之,否则建实例,actor name加字母前缀
|
||||||
val cref = context.child(sn)
|
val cref = context.child(sn)
|
||||||
cref match {
|
cref match {
|
||||||
@ -164,7 +164,7 @@ class TransProcessor(name: String, da:String, parent: ActorRef) extends Actor {
|
|||||||
//println(s"${pe.getSysTag} do invoke")
|
//println(s"${pe.getSysTag} do invoke")
|
||||||
//尝试从持久化恢复,找到对应的Deploy交易,并先执行之
|
//尝试从持久化恢复,找到对应的Deploy交易,并先执行之
|
||||||
val sr = ImpDataAccess.GetDataAccess(sTag)
|
val sr = ImpDataAccess.GetDataAccess(sTag)
|
||||||
val tidVal = sr.Get(WorldStateKeyPreFix+ cid)
|
val tidVal = sr.Get(WorldStateKeyPreFix+ tx_cid)
|
||||||
if(tidVal==null)
|
if(tidVal==null)
|
||||||
throw new SandboxException(ERR_INVOKE_CHAINCODE_NOT_EXIST)
|
throw new SandboxException(ERR_INVOKE_CHAINCODE_NOT_EXIST)
|
||||||
val txId = SerializeUtils.deserialiseJson(tidVal).asInstanceOf[String]
|
val txId = SerializeUtils.deserialiseJson(tidVal).asInstanceOf[String]
|
||||||
|
@ -65,7 +65,7 @@ object Compiler{
|
|||||||
*/
|
*/
|
||||||
class Compiler(targetDir: Option[File], bDebug:Boolean) {
|
class Compiler(targetDir: Option[File], bDebug:Boolean) {
|
||||||
//合约类名前缀
|
//合约类名前缀
|
||||||
val PRE_CLS_NAME = "sha"
|
val PRE_CLS_NAME = "SC_"
|
||||||
//反射工具对象
|
//反射工具对象
|
||||||
val tb = currentMirror.mkToolBox()
|
val tb = currentMirror.mkToolBox()
|
||||||
//源文件路径
|
//源文件路径
|
||||||
|
@ -36,7 +36,7 @@ import rep.sc.contract.ActionResult
|
|||||||
/**
|
/**
|
||||||
* @author c4w
|
* @author c4w
|
||||||
*/
|
*/
|
||||||
class SandboxJS(cid:String) extends Sandbox(cid){
|
class SandboxJS(cid:ChaincodeId) extends Sandbox(cid){
|
||||||
|
|
||||||
val sandbox= new ScriptEngineManager().getEngineByName("nashorn")
|
val sandbox= new ScriptEngineManager().getEngineByName("nashorn")
|
||||||
sandbox.put("shim",shim)
|
sandbox.put("shim",shim)
|
||||||
|
@ -36,9 +36,8 @@ import rep.sc.contract._
|
|||||||
/**
|
/**
|
||||||
* @author c4w
|
* @author c4w
|
||||||
*/
|
*/
|
||||||
class SandboxScala(cid:String) extends Sandbox(cid){
|
class SandboxScala(cid:ChaincodeId) extends Sandbox(cid){
|
||||||
var cobj:IContract = null
|
var cobj:IContract = null
|
||||||
val pcid = cid
|
|
||||||
|
|
||||||
def doTransaction(t:Transaction,from:ActorRef, da:String):DoTransactionResult ={
|
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)
|
val ctx = new ContractContext(shim,t)
|
||||||
//如果执行中出现异常,返回异常
|
//如果执行中出现异常,返回异常
|
||||||
try{
|
try{
|
||||||
val cid = getTXCId(t)
|
val tx_cid = getTXCId(t)
|
||||||
|
|
||||||
val r: ActionResult = t.`type` match {
|
val r: ActionResult = t.`type` match {
|
||||||
//如果cid对应的合约class不存在,根据code生成并加载该class
|
//如果cid对应的合约class不存在,根据code生成并加载该class
|
||||||
case Transaction.Type.CHAINCODE_DEPLOY =>
|
case Transaction.Type.CHAINCODE_DEPLOY =>
|
||||||
|
//当: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
|
//热加载code对应的class
|
||||||
val code = t.para.spec.get.codePackage
|
val code = t.para.spec.get.codePackage
|
||||||
|
val clazz = Compiler.compilef(code,tx_cid)
|
||||||
val clazz = Compiler.compilef(code,pcid)
|
|
||||||
cobj = clazz.getConstructor().newInstance().asInstanceOf[IContract]
|
cobj = clazz.getConstructor().newInstance().asInstanceOf[IContract]
|
||||||
//cobj = new ContractAssets()
|
//cobj = new ContractAssets()
|
||||||
cobj.init(ctx)
|
cobj.init(ctx)
|
||||||
//deploy返回chancode.name
|
//deploy返回chancode.name
|
||||||
//利用kv记住cid对应的txid,并增加kv操作日志,以便恢复deploy时能根据cid找到当时deploy的tx及其代码内容
|
//利用kv记住cid对应的txid,并增加kv操作日志,以便恢复deploy时能根据cid找到当时deploy的tx及其代码内容
|
||||||
val txid = ByteString.copyFromUtf8(t.id).toByteArray()
|
val txid = ByteString.copyFromUtf8(t.id).toByteArray()
|
||||||
val key = WorldStateKeyPreFix+ cid
|
shim.sr.Put(key_tx,txid)
|
||||||
shim.sr.Put(key,txid)
|
shim.ol.append(new Oper(key_tx, null, txid))
|
||||||
//ol value改为byte array
|
|
||||||
shim.ol.append(new Oper(key, 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)
|
new ActionResult(1,None)
|
||||||
|
}
|
||||||
//新建class实例并执行合约,传参为json数据
|
//新建class实例并执行合约,传参为json数据
|
||||||
//TODO case Transaction.Type.CHAINCODE_DESC 增加对合约描述的处理
|
//TODO case Transaction.Type.CHAINCODE_DESC 增加对合约描述的处理
|
||||||
case Transaction.Type.CHAINCODE_INVOKE =>
|
case Transaction.Type.CHAINCODE_INVOKE =>
|
||||||
|
@ -58,16 +58,11 @@ case class Transfer(from:String, to:String, amount:Int)
|
|||||||
* 根据action,找到对应的method,并将传入的json字符串parse为method需要的传入参数
|
* 根据action,找到对应的method,并将传入的json字符串parse为method需要的传入参数
|
||||||
*/
|
*/
|
||||||
def onAction(ctx: ContractContext,action:String, sdata:String ):ActionResult={
|
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 {
|
action match {
|
||||||
case "transfer" =>
|
case "transfer" =>
|
||||||
println(s"transfer oook")
|
|
||||||
transfer(ctx,json.extract[Transfer])
|
transfer(ctx,json.extract[Transfer])
|
||||||
case "set" =>
|
case "set" =>
|
||||||
println(s"set")
|
|
||||||
set(ctx, json.extract[Map[String,Int]])
|
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