mirror of
https://gitee.com/BTAJL/repchain.git
synced 2024-12-01 03:08:18 +08:00
Merge branch 'dev_wuwei1972'
This commit is contained in:
commit
5b1022cafc
@ -60,6 +60,9 @@ libraryDependencies ++= Seq(
|
||||
"com.typesafe.akka" %% "akka-http" % akkaHttpVersion,
|
||||
"com.typesafe.akka" %% "akka-http-spray-json" % akkaHttpVersion,
|
||||
"de.heikoseeberger" % "akka-http-json4s_2.11" % "1.16.1",
|
||||
"org.json4s" %% "json4s-native" % "3.5.4",
|
||||
"org.json4s" %% "json4s-jackson" % "3.5.4",
|
||||
|
||||
"ch.megard" %% "akka-http-cors" % "0.2.2"
|
||||
)
|
||||
|
||||
|
BIN
jks/mykeystore_5.jks
Normal file
BIN
jks/mykeystore_5.jks
Normal file
Binary file not shown.
203
scripts/api_req/assets5.xml
Normal file
203
scripts/api_req/assets5.xml
Normal file
@ -0,0 +1,203 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2018 Blockchain Technology and Application Joint Lab, 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.
|
||||
-->
|
||||
|
||||
<CSpec>
|
||||
<stype>1</stype>
|
||||
<idPath></idPath>
|
||||
<idName></idName>
|
||||
<iptFunc></iptFunc>
|
||||
<iptArgs>
|
||||
</iptArgs>
|
||||
<timeout>0</timeout>
|
||||
<secureContext>string</secureContext>
|
||||
<code>
|
||||
<![CDATA[
|
||||
|
||||
|
||||
package rep.sc.tpl
|
||||
|
||||
import rep.sc.contract._
|
||||
import org.json4s._
|
||||
import org.json4s.jackson.JsonMethods._
|
||||
import scala.reflect.ManifestFactory.classType
|
||||
import scala.collection.mutable.Map
|
||||
import org.json4s.native.Serialization
|
||||
import org.json4s.native.Serialization.{read, write}
|
||||
import org.json4s.{DefaultFormats, Formats, jackson}
|
||||
|
||||
/**
|
||||
* 供应链分账合约
|
||||
*/
|
||||
class SupplyTPL extends IContract {
|
||||
import rep.sc.tpl.SupplyType._
|
||||
|
||||
val SPLIT_CHAR = "_";
|
||||
val TPL_MODE = "_PM";
|
||||
implicit val formats = DefaultFormats
|
||||
|
||||
|
||||
def init(ctx: ContractContext){
|
||||
println(s"tid: $ctx.t.txid")
|
||||
}
|
||||
|
||||
/**
|
||||
* 追加确认签名 TODO 逻辑实现
|
||||
*/
|
||||
def confirmSign(ctx: ContractContext, data:IPTConfirm ):Object={
|
||||
null
|
||||
}
|
||||
/**
|
||||
* 取消追加确认签名 TODO 逻辑实现
|
||||
*/
|
||||
def cancelSign(ctx: ContractContext, data:IPTConfirm ):Object={
|
||||
null
|
||||
}
|
||||
/**
|
||||
* 设计方、原料方、生产方、销售方 签订对销售额的分成合约, 对于销售方账号+产品型号决定唯一的分账合约
|
||||
*/
|
||||
def signShare(ctx: ContractContext, data:IPTSignShare ):Object={
|
||||
val sid = data.account_sale +SPLIT_CHAR + data.product_id
|
||||
val pid = sid+TPL_MODE
|
||||
//签约输入持久化,默认的类型转换无法胜任,以json字符串形式持久化
|
||||
ctx.api.setVal(sid, write(data))
|
||||
ctx.api.setVal(pid, TPL.Share)
|
||||
sid
|
||||
}
|
||||
|
||||
def signFixed(ctx: ContractContext, data:IPTSignFixed ):Object={
|
||||
val sid = data.account_sale +SPLIT_CHAR + data.product_id
|
||||
val pid = sid+TPL_MODE
|
||||
//签约输入持久化
|
||||
ctx.api.setVal(sid, write(data))
|
||||
ctx.api.setVal(pid, TPL.Fixed)
|
||||
sid
|
||||
}
|
||||
|
||||
/**
|
||||
* 分账的调度方法,负责根据调用相应的分账模版, 传入模版定制参数和销售数据,进行分账
|
||||
*/
|
||||
def split(ctx: ContractContext, data:IPTSplit ):Object={
|
||||
//根据销售方账号和产品Id获得分账脚本
|
||||
val sid = data.account_sale +SPLIT_CHAR + data.product_id
|
||||
val pid = sid + TPL_MODE
|
||||
val tm = ctx.api.getVal(pid).asInstanceOf[String]
|
||||
|
||||
//根据签约时选择的分账方式模版,验证定制参数
|
||||
val mr = tm match {
|
||||
case TPL.Share =>
|
||||
val sp0 = ctx.api.getVal(sid)
|
||||
val sp = read[IPTSignShare](ctx.api.getVal(sid).asInstanceOf[String])
|
||||
splitShare(data.amount, sp.account_remain, sp.tpl_param)
|
||||
case TPL.Fixed =>
|
||||
val sp = read[IPTSignFixed](ctx.api.getVal(sid).asInstanceOf[String])
|
||||
splitFixedRatio(data.amount, sp.account_remain, sp.ratio)
|
||||
}
|
||||
//返回分账计算结果
|
||||
addToAccount(ctx, mr)
|
||||
mr
|
||||
}
|
||||
|
||||
/**
|
||||
* 将分账结果增加到账户并持久化
|
||||
*/
|
||||
def addToAccount(ctx: ContractContext, mr:Map[String,Int]){
|
||||
for ((k, v) <- mr) {
|
||||
val sk = ctx.api.getVal(k)
|
||||
var dk = if(sk==null) 0 else sk.toString.toInt
|
||||
ctx.api.setVal(k, dk+v)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 合约方法入口
|
||||
*/
|
||||
def onAction(ctx: ContractContext,action:String, sdata:String ):Object={
|
||||
val json = parse(sdata)
|
||||
|
||||
action match {
|
||||
case ACTION.SignShare =>
|
||||
signShare(ctx,json.extract[IPTSignShare])
|
||||
case ACTION.SignFixed =>
|
||||
signFixed(ctx,json.extract[IPTSignFixed])
|
||||
case ACTION.Split =>
|
||||
split(ctx, json.extract[IPTSplit])
|
||||
case ACTION.ConfirmSign =>
|
||||
confirmSign(ctx,json.extract[IPTConfirm])
|
||||
case ACTION.CancelSign =>
|
||||
cancelSign(ctx, json.extract[IPTConfirm])
|
||||
}
|
||||
}
|
||||
//TODO case Transaction.Type.CHAINCODE_DESC 增加对合约描述的处理
|
||||
def descAction(ctx: ContractContext,action:String, sdata:String ):String={
|
||||
val json = parse(sdata)
|
||||
null
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部函数, 获得分阶段的分成
|
||||
*/
|
||||
def getShare(sr: Int, ar: Array[ShareRatio]) : Int={
|
||||
var rv = 0
|
||||
for(el <- ar) {
|
||||
//击中金额范围
|
||||
if(sr > el.from && sr <= el.to) {
|
||||
//固定金额
|
||||
if(el.fixed > 0)
|
||||
rv = el.fixed
|
||||
else //按比例分成
|
||||
rv = (sr * el.ratio) .toInt
|
||||
}
|
||||
}
|
||||
rv
|
||||
}
|
||||
/**
|
||||
* 合约中内置多种分账模版,签约时可选择模版,如果出现新的分账模版,则部署一版新的合约
|
||||
* 分成模型, 除了销售方之外, 其他各方要求一个最低金额,分成按照金额阶段有所不同。
|
||||
*/
|
||||
def splitShare(sr: Int, account_remain:String, rule: ShareMap): Map[String,Int] ={
|
||||
//分账结果
|
||||
val rm : Map[String, Int] = Map()
|
||||
//分账余额
|
||||
var remain = sr
|
||||
for ((k, v) <- rule) {
|
||||
val rv = getShare(sr, v)
|
||||
rm += (k -> rv)
|
||||
remain -= rv
|
||||
}
|
||||
rm += (account_remain -> remain)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 各方固定比例分成,此模版仅仅为了合约对多模版的支持,可能无实际用途
|
||||
*/
|
||||
def splitFixedRatio(sr: Int, account_remain: String, mr:FixedMap): Map[String,Int] ={
|
||||
val rm : Map[String, Int] = Map()
|
||||
var remain = sr
|
||||
//根据固定分成
|
||||
for ((k, v) <- mr) {
|
||||
val rv = (sr* v ).toInt
|
||||
rm += (k -> rv)
|
||||
remain -= rv
|
||||
}
|
||||
//剩余的分给指定的余额账户
|
||||
rm += (account_remain -> remain)
|
||||
}
|
||||
}
|
||||
|
||||
]]>
|
||||
</code>
|
||||
<ctype>1</ctype>
|
||||
</CSpec>
|
@ -43,9 +43,8 @@ class NewContract extends IContract{
|
||||
null
|
||||
}
|
||||
|
||||
|
||||
def read(ctx: ContractContext, key: String):Any={
|
||||
ctx.api.getVal(key)
|
||||
def read(ctx: ContractContext, key: String):Object={
|
||||
ctx.api.getVal(key).toString
|
||||
}
|
||||
|
||||
def loadCert(ctx: ContractContext, cert: String): Unit = {
|
||||
@ -137,6 +136,9 @@ class NewContract extends IContract{
|
||||
case "replaceCert" =>
|
||||
println(s"replaceCert")
|
||||
replaceCert(ctx, json.extract[ReplaceCert])
|
||||
case "read"=>
|
||||
println(s"read")
|
||||
read(ctx, json.extract[String])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -178,10 +178,8 @@ class RestActor extends Actor with ModuleHelper with RepLogging {
|
||||
val sig = txr.signature.toByteArray
|
||||
val tOutSig1 = txr.withSignature(ByteString.EMPTY)
|
||||
val tOutSig = tOutSig1.withMetadata(ByteString.EMPTY)
|
||||
|
||||
val cid = ChaincodeID.fromAscii(txr.chaincodeID.toStringUtf8).name
|
||||
val certKey = WorldStateKeyPreFix + cid + "_" + PRE_CERT + txr.cert.toStringUtf8
|
||||
|
||||
try{
|
||||
var cert = ECDSASign.getCertWithCheck(txr.cert.toStringUtf8,certKey,pe.getSysTag)
|
||||
if(cert != None){
|
||||
|
@ -24,6 +24,7 @@ import com.google.protobuf.ByteString
|
||||
object Sha256 extends CryptographicHash{
|
||||
override val DigestSize: Int = 32
|
||||
def hash(input: Array[Byte]): Array[Byte] = MessageDigest.getInstance("SHA-256").digest(input)
|
||||
|
||||
def hashstr(input: Array[Byte]):String ={
|
||||
BytesHex.bytes2hex(hash(input))
|
||||
}
|
||||
|
@ -132,7 +132,6 @@ object PeerHelper {
|
||||
}catch{
|
||||
case e:RuntimeException => throw e
|
||||
}
|
||||
|
||||
t
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,6 @@ object BlockHelper {
|
||||
}catch{
|
||||
case e:RuntimeException => throw e
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -88,7 +87,6 @@ object BlockHelper {
|
||||
val tOutSig1 = t.withSignature(ByteString.EMPTY)
|
||||
val tOutSig = tOutSig1.withMetadata(ByteString.EMPTY)
|
||||
|
||||
|
||||
try{
|
||||
val cid = ChaincodeID.fromAscii(t.chaincodeID.toStringUtf8).name
|
||||
val certKey = IdxPrefix.WorldStateKeyPreFix + cid + "_" + "CERT_" + t.cert.toStringUtf8 // 普通用户证书的key
|
||||
@ -101,7 +99,6 @@ object BlockHelper {
|
||||
}catch{
|
||||
case e : RuntimeException => resultMsg = s"The transaction(${t.txid}) is not trusted${e.getMessage}"
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
|
@ -127,15 +127,25 @@ class PreloadTransactionModule(moduleName: String, transProcessor:ActorRef) exte
|
||||
logTime(s"Trans Preload End, Trans size ${transResult.size - errorCount}", CRFD_STEP._6_PRELOAD_END,
|
||||
getActorRef(ActorType.STATISTIC_COLLECTION))
|
||||
isPreload = false
|
||||
ImpDataPreloadMgr.Free(pe.getDBTag,blk.transactions.head.txid)
|
||||
clearCache() //是否是多余的,确保一定执行了
|
||||
freeSource
|
||||
schedulerLink = clearSched()
|
||||
}
|
||||
|
||||
def freeSource={
|
||||
if(blk != null){
|
||||
if(blk.transactions.size > 0){
|
||||
ImpDataPreloadMgr.Free(pe.getDBTag,blk.transactions.head.txid)
|
||||
}
|
||||
}
|
||||
clearCache() //是否是多余的,确保一定执行了
|
||||
}
|
||||
|
||||
override def receive = {
|
||||
|
||||
case PreTransBlock(blc, from,blkIdentifier) =>
|
||||
val preBlk = dataaccess.getBlockByHash(blk.previousBlockHash.toStringUtf8)
|
||||
freeSource
|
||||
|
||||
if((preBlk!=null && dataaccess.getBlockChainInfo().currentWorldStateHash == getBlkFromByte(preBlk).stateHash.toStringUtf8)
|
||||
|| blk.previousBlockHash == ByteString.EMPTY){
|
||||
blkIdentifier_src = blkIdentifier
|
||||
@ -214,10 +224,12 @@ class PreloadTransactionModule(moduleName: String, transProcessor:ActorRef) exte
|
||||
preLoadFeedBackInfo(false, blk, preloadFrom, pe.getMerk)
|
||||
blkIdentifier_src = ""
|
||||
isPreload = false
|
||||
ImpDataPreloadMgr.Free(pe.getDBTag,blk.transactions.head.txid)
|
||||
clearCache() //是否是多余的,确保一定执行了
|
||||
freeSource
|
||||
schedulerLink = clearSched()
|
||||
case false => logMsg(LOG_TYPE.INFO, "Preload trans timeout checked, successfully")
|
||||
case false => {
|
||||
freeSource
|
||||
logMsg(LOG_TYPE.INFO, "Preload trans timeout checked, successfully")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,7 @@ import java.io._
|
||||
import org.json4s._
|
||||
import org.json4s.jackson.JsonMethods._
|
||||
import rep.crypto.Sha256
|
||||
|
||||
import rep.app.conf.SystemProfile
|
||||
import scala.reflect.runtime.currentMirror
|
||||
import scala.tools.reflect.ToolBox
|
||||
@ -120,10 +121,20 @@ class Compiler(targetDir: Option[File], bDebug:Boolean) {
|
||||
* @return 编译生成的类定义
|
||||
*/
|
||||
def compilef(pcode: String, cid: String): Class[_]= {
|
||||
val p1 = pcode.indexOf("extends IContract{")
|
||||
//去掉package声明,将class放在default路径下
|
||||
var p0 = pcode.indexOf("import")
|
||||
//第一个定位点应加强容错能力,允许空白字符
|
||||
val pattern = "extends\\s+IContract\\s*\\{".r
|
||||
val p1str = pattern.findFirstIn(pcode).get
|
||||
val p1 = pcode.indexOf(p1str)
|
||||
val p2 = pcode.lastIndexOf("}")
|
||||
val p3 = pcode.lastIndexOf("class ",p1)
|
||||
|
||||
//可能不存在import指令
|
||||
if(p0.equals(-1))
|
||||
p0 = p3
|
||||
if(p1.equals(-1) || p1.equals(-1) || p1.equals(-1))
|
||||
throw new RuntimeException("合约语法错误")
|
||||
val className = if(cid!=null) PRE_CLS_NAME+cid else classNameForCode(pcode)
|
||||
try{
|
||||
val cl = Class.forName(className)
|
||||
@ -132,7 +143,7 @@ class Compiler(targetDir: Option[File], bDebug:Boolean) {
|
||||
case e:Throwable =>
|
||||
findClass(className).getOrElse {
|
||||
//替换类名为 hash256
|
||||
val ncode = pcode.substring(0,p3) + "class "+className+ " "+pcode.substring(p1,p2+1)
|
||||
val ncode = pcode.substring(p0,p3) + "class "+className+ " "+pcode.substring(p1,p2+1)
|
||||
//+"\nscala.reflect.classTag[ContractAssets2].runtimeClass"
|
||||
if(path_source!=null)
|
||||
saveCode(className,ncode)
|
||||
|
@ -72,6 +72,7 @@ class SandboxScala(cid:String) extends Sandbox(cid){
|
||||
shim.ol.append(new Oper(key, null, txid))
|
||||
encodeJson(cid)
|
||||
//新建class实例并执行合约,传参为json数据
|
||||
//TODO case Transaction.Type.CHAINCODE_DESC 增加对合约描述的处理
|
||||
case Transaction.Type.CHAINCODE_INVOKE =>
|
||||
//获得合约action
|
||||
val action = cs.ctorMsg.get.function
|
||||
|
171
src/main/scala/rep/sc/tpl/SupplyTPL.scala
Normal file
171
src/main/scala/rep/sc/tpl/SupplyTPL.scala
Normal file
@ -0,0 +1,171 @@
|
||||
|
||||
|
||||
package rep.sc.tpl
|
||||
|
||||
import rep.sc.contract._
|
||||
import org.json4s._
|
||||
import org.json4s.jackson.JsonMethods._
|
||||
import scala.reflect.ManifestFactory.classType
|
||||
import scala.collection.mutable.Map
|
||||
import org.json4s.native.Serialization
|
||||
import org.json4s.native.Serialization.{read, write}
|
||||
import org.json4s.{DefaultFormats, Formats, jackson}
|
||||
|
||||
/**
|
||||
* 供应链分账合约
|
||||
*/
|
||||
class SupplyTPL extends IContract {
|
||||
import rep.sc.tpl.SupplyType._
|
||||
|
||||
val SPLIT_CHAR = "_";
|
||||
val TPL_MODE = "_PM";
|
||||
implicit val formats = DefaultFormats
|
||||
|
||||
|
||||
def init(ctx: ContractContext){
|
||||
println(s"tid: $ctx.t.txid")
|
||||
}
|
||||
|
||||
/**
|
||||
* 追加确认签名 TODO 逻辑实现
|
||||
*/
|
||||
def confirmSign(ctx: ContractContext, data:IPTConfirm ):Object={
|
||||
null
|
||||
}
|
||||
/**
|
||||
* 取消追加确认签名 TODO 逻辑实现
|
||||
*/
|
||||
def cancelSign(ctx: ContractContext, data:IPTConfirm ):Object={
|
||||
null
|
||||
}
|
||||
/**
|
||||
* 设计方、原料方、生产方、销售方 签订对销售额的分成合约, 对于销售方账号+产品型号决定唯一的分账合约
|
||||
*/
|
||||
def signShare(ctx: ContractContext, data:IPTSignShare ):Object={
|
||||
val sid = data.account_sale +SPLIT_CHAR + data.product_id
|
||||
val pid = sid+TPL_MODE
|
||||
//签约输入持久化,默认的类型转换无法胜任,以json字符串形式持久化
|
||||
ctx.api.setVal(sid, write(data))
|
||||
ctx.api.setVal(pid, TPL.Share)
|
||||
sid
|
||||
}
|
||||
|
||||
def signFixed(ctx: ContractContext, data:IPTSignFixed ):Object={
|
||||
val sid = data.account_sale +SPLIT_CHAR + data.product_id
|
||||
val pid = sid+TPL_MODE
|
||||
//签约输入持久化
|
||||
ctx.api.setVal(sid, write(data))
|
||||
ctx.api.setVal(pid, TPL.Fixed)
|
||||
sid
|
||||
}
|
||||
|
||||
/**
|
||||
* 分账的调度方法,负责根据调用相应的分账模版, 传入模版定制参数和销售数据,进行分账
|
||||
*/
|
||||
def split(ctx: ContractContext, data:IPTSplit ):Object={
|
||||
//根据销售方账号和产品Id获得分账脚本
|
||||
val sid = data.account_sale +SPLIT_CHAR + data.product_id
|
||||
val pid = sid + TPL_MODE
|
||||
val tm = ctx.api.getVal(pid).asInstanceOf[String]
|
||||
|
||||
//根据签约时选择的分账方式模版,验证定制参数
|
||||
val mr = tm match {
|
||||
case TPL.Share =>
|
||||
val sp0 = ctx.api.getVal(sid)
|
||||
val sp = read[IPTSignShare](ctx.api.getVal(sid).asInstanceOf[String])
|
||||
splitShare(data.amount, sp.account_remain, sp.tpl_param)
|
||||
case TPL.Fixed =>
|
||||
val sp = read[IPTSignFixed](ctx.api.getVal(sid).asInstanceOf[String])
|
||||
splitFixedRatio(data.amount, sp.account_remain, sp.ratio)
|
||||
}
|
||||
//返回分账计算结果
|
||||
addToAccount(ctx, mr)
|
||||
mr
|
||||
}
|
||||
|
||||
/**
|
||||
* 将分账结果增加到账户并持久化
|
||||
*/
|
||||
def addToAccount(ctx: ContractContext, mr:Map[String,Int]){
|
||||
for ((k, v) <- mr) {
|
||||
val sk = ctx.api.getVal(k)
|
||||
var dk = if(sk==null) 0 else sk.toString.toInt
|
||||
ctx.api.setVal(k, dk+v)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 合约方法入口
|
||||
*/
|
||||
def onAction(ctx: ContractContext,action:String, sdata:String ):Object={
|
||||
val json = parse(sdata)
|
||||
|
||||
action match {
|
||||
case ACTION.SignShare =>
|
||||
signShare(ctx,json.extract[IPTSignShare])
|
||||
case ACTION.SignFixed =>
|
||||
signFixed(ctx,json.extract[IPTSignFixed])
|
||||
case ACTION.Split =>
|
||||
split(ctx, json.extract[IPTSplit])
|
||||
case ACTION.ConfirmSign =>
|
||||
confirmSign(ctx,json.extract[IPTConfirm])
|
||||
case ACTION.CancelSign =>
|
||||
cancelSign(ctx, json.extract[IPTConfirm])
|
||||
}
|
||||
}
|
||||
//TODO case Transaction.Type.CHAINCODE_DESC 增加对合约描述的处理
|
||||
def descAction(ctx: ContractContext,action:String, sdata:String ):String={
|
||||
val json = parse(sdata)
|
||||
null
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部函数, 获得分阶段的分成
|
||||
*/
|
||||
def getShare(sr: Int, ar: Array[ShareRatio]) : Int={
|
||||
var rv = 0
|
||||
for(el <- ar) {
|
||||
//击中金额范围
|
||||
if(sr > el.from && sr <= el.to) {
|
||||
//固定金额
|
||||
if(el.fixed > 0)
|
||||
rv = el.fixed
|
||||
else //按比例分成
|
||||
rv = (sr * el.ratio) .toInt
|
||||
}
|
||||
}
|
||||
rv
|
||||
}
|
||||
/**
|
||||
* 合约中内置多种分账模版,签约时可选择模版,如果出现新的分账模版,则部署一版新的合约
|
||||
* 分成模型, 除了销售方之外, 其他各方要求一个最低金额,分成按照金额阶段有所不同。
|
||||
*/
|
||||
def splitShare(sr: Int, account_remain:String, rule: ShareMap): Map[String,Int] ={
|
||||
//分账结果
|
||||
val rm : Map[String, Int] = Map()
|
||||
//分账余额
|
||||
var remain = sr
|
||||
for ((k, v) <- rule) {
|
||||
val rv = getShare(sr, v)
|
||||
rm += (k -> rv)
|
||||
remain -= rv
|
||||
}
|
||||
rm += (account_remain -> remain)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 各方固定比例分成,此模版仅仅为了合约对多模版的支持,可能无实际用途
|
||||
*/
|
||||
def splitFixedRatio(sr: Int, account_remain: String, mr:FixedMap): Map[String,Int] ={
|
||||
val rm : Map[String, Int] = Map()
|
||||
var remain = sr
|
||||
//根据固定分成
|
||||
for ((k, v) <- mr) {
|
||||
val rv = (sr* v ).toInt
|
||||
rm += (k -> rv)
|
||||
remain -= rv
|
||||
}
|
||||
//剩余的分给指定的余额账户
|
||||
rm += (account_remain -> remain)
|
||||
}
|
||||
}
|
53
src/main/scala/rep/sc/tpl/SupplyType.scala
Normal file
53
src/main/scala/rep/sc/tpl/SupplyType.scala
Normal file
@ -0,0 +1,53 @@
|
||||
package rep.sc.tpl
|
||||
|
||||
import scala.collection.mutable.Map
|
||||
|
||||
/**
|
||||
* 分账接口
|
||||
*/
|
||||
trait ISupplySplit {
|
||||
/**
|
||||
* @param sr 销售收入
|
||||
* @param accounts 参与分账的账户
|
||||
* @return 分账结果
|
||||
*/
|
||||
def split(sr: Int, accounts: Array[String]): Array[Int]
|
||||
}
|
||||
|
||||
|
||||
package object SupplyType {
|
||||
object ACTION {
|
||||
val SignShare = "SignShare"
|
||||
val SignFixed = "SignFixed"
|
||||
val ConfirmSign = "ConfirmSign"
|
||||
val CancelSign = "CancelSign"
|
||||
val Split = "Split"
|
||||
|
||||
}
|
||||
|
||||
object TPL {
|
||||
val Share = "Share"
|
||||
val Fixed = "Fixed"
|
||||
}
|
||||
/**
|
||||
* 按销售收入分段分成/固定值 的设置项
|
||||
*/
|
||||
case class ShareRatio(from: Int, to: Int, ratio: Double, fixed: Int)
|
||||
//多个账户的分段分成定义
|
||||
type ShareMap = scala.collection.mutable.Map[String, Array[ShareRatio]]
|
||||
type FixedMap = scala.collection.mutable.Map[String,Double]
|
||||
/**
|
||||
* 签署分成合约的输入参数
|
||||
* @param account_sale 提交销售数据的账号
|
||||
* @param
|
||||
*/
|
||||
case class IPTSignShare(account_sale :String, product_id: String, account_remain :String, tpl_param: ShareMap)
|
||||
//固定分账比例
|
||||
case class IPTSignFixed(account_sale :String, product_id: String, account_remain :String, ratio:Map[String,Double] )
|
||||
//触发分账的输入参数
|
||||
//TODO 如何防止重复提交?
|
||||
case class IPTSplit(account_sale :String, product_id:String, amount:Int)
|
||||
|
||||
case class IPTConfirm(account: String, tx_id:String)
|
||||
}
|
||||
|
@ -120,6 +120,7 @@ public class pathUtil {
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
public static int bytesToInt(byte[] inputs){
|
||||
if(inputs == null) return 0;
|
||||
if(inputs.length < 4) return 0;
|
||||
|
@ -0,0 +1,127 @@
|
||||
import org.json4s._
|
||||
import org.json4s.jackson.JsonMethods._
|
||||
import rep.sc.contract._
|
||||
import rep.storage.FakeStorage.Key
|
||||
|
||||
/**
|
||||
* 资产管理合约
|
||||
*/
|
||||
|
||||
class sha0bfbe2faf858dd495e712fb0f897dd66082f06b879fa21a80fcc2acbc199b8d7 extends IContract{
|
||||
case class Transfer(from:String, to:String, amount:Int)
|
||||
// case class Proof(key:String, content:String)
|
||||
case class ReplaceCert(cert:String, addr:String)
|
||||
// case class Cert(cert:String, info:String)
|
||||
|
||||
implicit val formats = DefaultFormats
|
||||
|
||||
def init(ctx: ContractContext){
|
||||
println(s"tid: $ctx.t.txid")
|
||||
}
|
||||
|
||||
def set(ctx: ContractContext, data:Map[String,Int]):Object={
|
||||
println(s"set data:$data")
|
||||
for((k,v)<-data){
|
||||
ctx.api.setVal(k, v)
|
||||
}
|
||||
null
|
||||
}
|
||||
|
||||
|
||||
def read(ctx: ContractContext, key: String):Any={
|
||||
ctx.api.getVal(key)
|
||||
}
|
||||
|
||||
def loadCert(ctx: ContractContext, cert: String): Unit = {
|
||||
ctx.api.loadCert(cert);
|
||||
print("cert:"+cert);
|
||||
}
|
||||
|
||||
def write(ctx: ContractContext, data:Map[String,Int]):Object = {
|
||||
for((k,v)<-data){
|
||||
ctx.api.setVal(k, v)
|
||||
}
|
||||
null
|
||||
}
|
||||
|
||||
def put_proof(ctx: ContractContext, data:Map[String,Any]):Object={
|
||||
//先检查该hash是否已经存在,如果已存在,抛异常
|
||||
for((k,v)<-data){
|
||||
var pv0 = ctx.api.getVal(k)
|
||||
if(pv0 != null)
|
||||
throw new Exception("["+k+"]已存在,当前值["+pv0+"]");
|
||||
ctx.api.setVal(k,v);
|
||||
print("putProof:"+k+":"+v);
|
||||
}
|
||||
"put_proof ok"
|
||||
}
|
||||
|
||||
|
||||
|
||||
def signup(ctx: ContractContext, data:Map[String,String]):Object = {
|
||||
var addr = ""
|
||||
for((k,v)<-data){
|
||||
ctx.api.check(ctx.t.cert.toStringUtf8,ctx.t)
|
||||
addr = ctx.api.signup(k,v)
|
||||
}
|
||||
addr
|
||||
}
|
||||
|
||||
def destroyCert(ctx: ContractContext, certAddr: String): Object = {
|
||||
println(s"destroy cert->addr:$certAddr")
|
||||
ctx.api.check(ctx.t.cert.toStringUtf8,ctx.t) //ctx中自带交易内容
|
||||
ctx.api.destroyCert(certAddr);
|
||||
"destory scuccess"
|
||||
}
|
||||
|
||||
def replaceCert(ctx: ContractContext, data:ReplaceCert): Object = {
|
||||
val cert = data.cert
|
||||
val addr = data.addr
|
||||
ctx.api.check(ctx.t.cert.toStringUtf8,ctx.t)
|
||||
ctx.api.replaceCert(cert,addr); // 返回短地址
|
||||
}
|
||||
|
||||
def transfer(ctx: ContractContext, data:Transfer):Object={
|
||||
val sfrom = ctx.api.getVal(data.from)
|
||||
var dfrom =sfrom.toString.toInt
|
||||
if(dfrom < data.amount)
|
||||
throw new Exception("余额不足")
|
||||
var dto = ctx.api.getVal(data.to).toString.toInt
|
||||
//if(dto==null) dto = 0;
|
||||
|
||||
ctx.api.setVal(data.from,dfrom - data.amount)
|
||||
ctx.api.setVal(data.to,dto + data.amount)
|
||||
"transfer ok"
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据action,找到对应的method,并将传入的json字符串parse为method需要的传入参数
|
||||
*/
|
||||
def onAction(ctx: ContractContext,action:String, sdata:String ):Object={
|
||||
//println(s"onAction---")
|
||||
//return "transfer ok"
|
||||
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]])
|
||||
case "put_proof" =>
|
||||
println(s"put_proof")
|
||||
put_proof(ctx, json.extract[Map[String,Any]])
|
||||
case "signup" =>
|
||||
println(s"signup")
|
||||
signup(ctx, json.extract[Map[String,String]])
|
||||
case "destroyCert" =>
|
||||
println(s"destroyCert")
|
||||
destroyCert(ctx, json.extract[String])
|
||||
case "replaceCert" =>
|
||||
println(s"replaceCert")
|
||||
replaceCert(ctx, json.extract[ReplaceCert])
|
||||
}
|
||||
}
|
||||
|
||||
}
|
93
src/test/scala/rep/sc/SplitSpec.scala
Normal file
93
src/test/scala/rep/sc/SplitSpec.scala
Normal file
@ -0,0 +1,93 @@
|
||||
package rep.sc
|
||||
import org.scalatest._
|
||||
import rep.sc.tpl.SupplyTPL
|
||||
import prop._
|
||||
import scala.collection.immutable._
|
||||
import scala.collection.mutable.Map
|
||||
|
||||
import org.json4s._
|
||||
import org.json4s.JsonDSL._
|
||||
import org.json4s.jackson.JsonMethods._
|
||||
import org.json4s.{DefaultFormats, Formats, jackson}
|
||||
import org.json4s.native.Serialization.writePretty
|
||||
import org.json4s.native.Serialization
|
||||
import org.json4s.native.Serialization.{read, write}
|
||||
|
||||
|
||||
class SplitSpec extends PropSpec with TableDrivenPropertyChecks with Matchers {
|
||||
import rep.sc.tpl.SupplyType._
|
||||
import rep.sc.tpl.SupplyType.FixedMap
|
||||
implicit val formats = DefaultFormats
|
||||
//implicit val formats = Serialization.formats(NoTypeHints)
|
||||
|
||||
val fm :FixedMap = Map("A" -> 0.2, "B"-> 0.2, "C"-> 0.1, "D" -> 0.1)
|
||||
val sm :ShareMap = Map("A" -> Array(new ShareRatio(0,100,0.1,0), new ShareRatio(100,10000,0.15,0)),
|
||||
"B" -> Array(new ShareRatio(0,10000,0,10)),
|
||||
"C" -> Array(new ShareRatio(0,10000,0.1,0)),
|
||||
"D" -> Array(new ShareRatio(0,100,0,10), new ShareRatio(100,10000,0.15,0)))
|
||||
val account_remain = "R"
|
||||
val account_sales = "S"
|
||||
val product_id = "P201806270001"
|
||||
|
||||
val s1 = new SupplyTPL
|
||||
|
||||
val examples =
|
||||
Table(
|
||||
"sr",
|
||||
100,
|
||||
200,
|
||||
500,
|
||||
1000
|
||||
)
|
||||
property("JSon format works for IPTSignShare") {
|
||||
val ipt1 = new IPTSignShare(account_sales,product_id,account_remain,sm)
|
||||
val jstr = writePretty(ipt1)
|
||||
println(s"${jstr}")
|
||||
val ipt2 = read[IPTSignShare](jstr)
|
||||
ipt2.account_remain should be (ipt1.account_remain)
|
||||
ipt2.tpl_param("A").length should be (ipt1.tpl_param("A").length)
|
||||
ipt2.tpl_param("A")(0) should be (ipt1.tpl_param("A")(0))
|
||||
}
|
||||
property("JSon format works for IPTSignFixed") {
|
||||
val ipt1 = new IPTSignFixed(account_sales,product_id,account_remain,fm)
|
||||
val jstr = write(ipt1)
|
||||
println(s"${jstr}")
|
||||
val ipt2 = read[IPTSignFixed](jstr)
|
||||
ipt2.account_remain should be (ipt1.account_remain)
|
||||
ipt2.ratio should be (ipt1.ratio)
|
||||
}
|
||||
property("JSon format works for IPTSplit") {
|
||||
val ipt1 = new IPTSplit(account_sales,product_id,1000)
|
||||
val jstr = write(ipt1)
|
||||
println(s"${jstr}")
|
||||
val ipt2 = read[IPTSplit](jstr)
|
||||
ipt2.product_id should be (ipt1.product_id)
|
||||
}
|
||||
property(" Splitting sales-revenue by splitFixedRatio") {
|
||||
forAll(examples) { sr =>
|
||||
val rm = s1.splitFixedRatio(sr, account_remain,fm)
|
||||
var total = 0
|
||||
print(s"total:${sr} ")
|
||||
for ((k, v) <- rm) {
|
||||
total += v
|
||||
print(s" ${k}: ${v} ")
|
||||
}
|
||||
println("")
|
||||
total should be (sr)
|
||||
}
|
||||
}
|
||||
property(" Splitting sales-revenue by splitShare") {
|
||||
forAll(examples) { sr =>
|
||||
val rm = s1.splitShare(sr, account_remain,sm)
|
||||
var total = 0
|
||||
print(s"total:${sr} ")
|
||||
for ((k, v) <- rm) {
|
||||
total += v
|
||||
print(s" ${k}: ${v} ")
|
||||
}
|
||||
println("")
|
||||
total should be (sr)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
187
src/test/scala/rep/sc/SupplySpec.scala
Normal file
187
src/test/scala/rep/sc/SupplySpec.scala
Normal file
@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright 2018 Blockchain Technology and Application Joint Lab, 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.app.system.ClusterSystem
|
||||
import rep.app.system.ClusterSystem.InitType
|
||||
|
||||
import rep.network.PeerHelper.transactionCreator
|
||||
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.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}
|
||||
|
||||
/** 合约容器实现的单元测试
|
||||
* @author c4w
|
||||
* @param _system 测试用例所在的actor System.
|
||||
*
|
||||
*/
|
||||
class SupplySpec(_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("1", InitType.MULTI_INIT, false).getConf))
|
||||
|
||||
override def afterAll: Unit = Await.ready(system.terminate(), Duration.Inf)
|
||||
|
||||
//Scala实现的资产管理合约测试,包括合约的部署、调用、结果返回验证
|
||||
"container" should "deploy supply contract and call it for splitting then" in {
|
||||
val sysName = "1"
|
||||
val dbTag = "1"
|
||||
//建立PeerManager实例是为了调用transactionCreator(需要用到密钥签名),无他
|
||||
val pm = system.actorOf(ModuleManager.props("pm", sysName))
|
||||
//加载合约脚本
|
||||
val s1 = scala.io.Source.fromFile("src/main/scala/rep/sc/tpl/SupplyTPL.scala")
|
||||
val l1 = try s1.mkString finally s1.close()
|
||||
|
||||
val fm :FixedMap= Map("A" -> 0.2, "B"-> 0.2, "C"-> 0.1, "D" -> 0.1)
|
||||
val sm :ShareMap = Map("A" -> Array(new ShareRatio(0,100,0.1,0), new ShareRatio(100,10000,0.15,0)),
|
||||
"B" -> Array(new ShareRatio(0,10000,0,10)),
|
||||
"C" -> Array(new ShareRatio(0,10000,0.1,0)),
|
||||
"D" -> Array(new ShareRatio(0,100,0,10), new ShareRatio(100,10000,0.15,0)))
|
||||
val account_remain = "R"
|
||||
val account_sales1 = "S1"
|
||||
val account_sales2 = "S2"
|
||||
val product_id = "P201806270001"
|
||||
|
||||
//构造签约交易合约模版1输入json字符串,销售1选择了合约模版1
|
||||
val ipt2 = new IPTSignFixed(account_sales1,product_id,account_remain,fm)
|
||||
val l2 = write(ipt2)
|
||||
|
||||
//构造签约交易合约模版2输入json字符串,,销售2选择了合约模版2
|
||||
val ipt3 = new IPTSignShare(account_sales2,product_id,account_remain,sm)
|
||||
val l3 = writePretty(ipt3)
|
||||
|
||||
//准备探针以验证调用返回结果
|
||||
val probe = TestProbe()
|
||||
val db = ImpDataAccess.GetDataAccess(sysName)
|
||||
var sandbox = system.actorOf(TransProcessor.props("sandbox", "", probe.ref))
|
||||
//生成deploy交易
|
||||
val t1 = transactionCreator(sysName, rep.protos.peer.Transaction.Type.CHAINCODE_DEPLOY,
|
||||
"", "", List(), l1, None, rep.protos.peer.ChaincodeSpec.CodeType.CODE_SCALA)
|
||||
|
||||
val msg_send1 = new DoTransaction(t1, probe.ref, "")
|
||||
probe.send(sandbox, msg_send1)
|
||||
val msg_recv1 = probe.expectMsgType[Sandbox.DoTransactionResult](1000.seconds)
|
||||
val ol1 = msg_recv1.ol
|
||||
val ol1str = compactJson(ol1)
|
||||
|
||||
//生成invoke交易
|
||||
//获取deploy生成的chainCodeId
|
||||
//初始化资产
|
||||
val cname = t1.payload.get.chaincodeID.get.name
|
||||
val t2 = transactionCreator(sysName, rep.protos.peer.Transaction.Type.CHAINCODE_INVOKE,
|
||||
"", ACTION.SignFixed, Seq(l2), "", Option(cname))
|
||||
|
||||
val msg_send2 = new DoTransaction(t2, probe.ref, "")
|
||||
probe.send(sandbox, msg_send2)
|
||||
val msg_recv2 = probe.expectMsgType[Sandbox.DoTransactionResult](1000.seconds)
|
||||
|
||||
val t3 = transactionCreator(sysName, rep.protos.peer.Transaction.Type.CHAINCODE_INVOKE,
|
||||
"", ACTION.SignShare, Seq(l3), "", Option(cname))
|
||||
val msg_send3 = new DoTransaction(t3, probe.ref, "")
|
||||
probe.send(sandbox, msg_send3)
|
||||
val msg_recv3 = probe.expectMsgType[Sandbox.DoTransactionResult](1000.seconds)
|
||||
|
||||
//测试各种金额下的分账结果
|
||||
val sr = Array(100, 200, 500, 1000)
|
||||
for (el<- sr) {
|
||||
//构造分账交易
|
||||
val ipt4 = new IPTSplit(account_sales1,product_id,el)
|
||||
val l4 = write(ipt4)
|
||||
val t4 = transactionCreator(sysName, rep.protos.peer.Transaction.Type.CHAINCODE_INVOKE,
|
||||
"", ACTION.Split, Seq(l4), "", Option(cname))
|
||||
val msg_send4 = new DoTransaction(t4, probe.ref, "")
|
||||
|
||||
probe.send(sandbox, msg_send4)
|
||||
val msg_recv4 = probe.expectMsgType[Sandbox.DoTransactionResult](1000.seconds)
|
||||
val ol4 = msg_recv4.ol
|
||||
val ol4str = compactJson(ol4)
|
||||
println(s"oper log:${ol4str}")
|
||||
//获得交易返回值
|
||||
val jv4 = msg_recv4.r.asInstanceOf[JValue]
|
||||
val rv4 = jv4.extract[Map[String,Int]]
|
||||
println(rv4)
|
||||
//分账之后总额应保持一致
|
||||
var total = 0
|
||||
for ((k, v) <- rv4) {
|
||||
total += v
|
||||
}
|
||||
total should be(el)
|
||||
}
|
||||
|
||||
for (el<- sr) {
|
||||
//构造分账交易
|
||||
val ipt4 = new IPTSplit(account_sales2,product_id,el)
|
||||
val l4 = write(ipt4)
|
||||
val t4 = transactionCreator(sysName, rep.protos.peer.Transaction.Type.CHAINCODE_INVOKE,
|
||||
"", ACTION.Split, Seq(l4), "", Option(cname))
|
||||
val msg_send4 = new DoTransaction(t4, probe.ref, "")
|
||||
|
||||
probe.send(sandbox, msg_send4)
|
||||
val msg_recv4 = probe.expectMsgType[Sandbox.DoTransactionResult](1000.seconds)
|
||||
val ol4 = msg_recv4.ol
|
||||
val ol4str = compactJson(ol4)
|
||||
println(s"oper log:${ol4str}")
|
||||
//获得交易返回值
|
||||
//获得交易返回值
|
||||
val jv4 = msg_recv4.r.asInstanceOf[JValue]
|
||||
val rv4 = jv4.extract[Map[String,Int]]
|
||||
println(rv4)
|
||||
//分账之后总额应保持一致
|
||||
var total = 0
|
||||
for ((k, v) <- rv4) {
|
||||
total += v
|
||||
}
|
||||
total should be(el)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user