From 9a80f8fb11bd732f657a930daea216e3bb3a53c4 Mon Sep 17 00:00:00 2001 From: CaiHQ Date: Fri, 22 Apr 2022 13:47:42 +0800 Subject: [PATCH] release 0.4.0 --- .gitignore | 3 + README.md | 70 ++-- build.gradle | 105 ++++++ .../java/org/bdware/ypkdeploy/HTTPTool.java | 137 ++++++++ .../ypkdeploy/SmartContractClientExt.java | 308 ++++++++++++++++++ 5 files changed, 591 insertions(+), 32 deletions(-) create mode 100644 .gitignore create mode 100755 build.gradle create mode 100644 src/main/java/org/bdware/ypkdeploy/HTTPTool.java create mode 100644 src/main/java/org/bdware/ypkdeploy/SmartContractClientExt.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4297b46 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +deployconfig.json +.DS_Store +/build/ \ No newline at end of file diff --git a/README.md b/README.md index 0bdfeaa..807105a 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,43 @@ -# ypk-deploy-tool +# 使用说明 +1) 在`build.gradle`中进行配置。 + ```groovy + buildscript { + repositories { + mavenLocal() + mavenCentral() + } + dependencies { + classpath "org.bdware.bdcontract:ypk-deploy-tool:0.3.0" + } +} +//.... +task deploy(dependsOn: ["xxx"]) { + doLast { + org.bdware.ypkdeploy.HTTPTool.batchRun("./xxx/deployconfig.json", true) + } +} +``` +2) 配置`./xxx/deployconfig.json`文件。 +参数说明: -#### 介绍 -ypk-deploy-tool +`host`为运行了bdagent的服务端的ip -#### 软件架构 -软件架构说明 +`agentPort`为端口。 +`privateKey/publicKey`为有部署权限的一组SM2KeyPair -#### 安装教程 +`ypkPath`是待部署的ypk路径。 -1. xxxx -2. xxxx -3. xxxx - -#### 使用说明 - -1. xxxx -2. xxxx -3. xxxx - -#### 参与贡献 - -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request - - -#### 特技 - -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 -5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) +`deployconfig.json`配置示例: +```json +{ + "host": "192.168.x.x", + "agentPort": 18000, + "privateKey": "5895c18430dd...", + "publicKey": "04d1924329f72ced14...", + "ypkPath": "/path/to/todeploy.ypk", + "killBeforeStart": "ContractName", + "createParam": { + } +} +``` \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100755 index 0000000..2db7d53 --- /dev/null +++ b/build.gradle @@ -0,0 +1,105 @@ +plugins { + id 'java' + id 'java-library' + id 'maven-publish' + id 'signing' +} +group 'org.bdware.bdcontract' +version '0.4.0' +sourceCompatibility = 1.8 + +repositories { + mavenCentral() + mavenLocal() +} + +dependencies { + implementation 'com.google.code.gson:gson:2.8.8' + implementation 'org.bdware.bdcontract:gmhelper:0.1.0' + implementation 'org.bdware.bdcontract:sdk-java:1.0.0' + implementation 'org.bouncycastle:bcpkix-jdk15on:1.69' + implementation 'org.bouncycastle:bcprov-jdk15on:1.69' + implementation 'org.apache.httpcomponents:httpclient:4.5.13' + implementation 'org.apache.httpcomponents:httpmime:4.5.13' + testImplementation 'junit:junit:4.13.2' +} + +task classJar(type: Jar, dependsOn: classes) { + classifier = "jar" +} +task sourceJar(type: Jar, dependsOn: classes) { + archiveClassifier = "sources" + classifier = "sources" + from sourceSets.main.allSource +} + +tasks.withType(Javadoc) { + options.addStringOption('Xdoclint:none', '-quiet') +} + +task javadocJar(type: Jar, dependsOn: javadoc) { + archiveClassifier = 'javadoc' + classifier = "javadoc" + exclude { + details -> details.file.getAbsolutePath().contains("/gm/") + } + from javadoc.destinationDir +} + +publishing { + publications { + mavenJava(MavenPublication) { + groupId project.group + artifactId "ypk-deploy-tool" + version "${version}" + from components.java + artifact sourceJar + artifact javadocJar + artifact classJar + pom { + name = "bdware-ypk-deploy-tool" + description = "ypk-deploy-tool" + url = "https://gitee.com/BDWare/ypk-deploy-tool" + licenses { + license { + name = "Mulan PSL v2" + url = "http://license.coscl.org.cn/MulanPSL2" + } + } + developers { + developer { + id = "dataware" + email = "caihq@pku.edu.cn" + } + } + scm { + connection = "scm:git:https://gitee.com/BDWare/ypk-deploy-tool.git" + developerConnection = "scm:git:https://gitee.com/BDWare/ypk-deploy-tool.git" + url = "https://gitee.com/BDWare/ypk-deploy-tool" + } + } + + } + } + repositories { + maven { + name 'bdwareSnapshotRepository' + url 'https://oss.sonatype.org/content/repositories/snapshots' + credentials { + username = "${NEXUS_USERNAME}" + password = "${NEXUS_PASSWORD}" + } + } + maven { + name 'bdwareRepository' + url 'https://oss.sonatype.org/service/local/staging/deploy/maven2' + credentials { + username = "${NEXUS_USERNAME}" + password = "${NEXUS_PASSWORD}" + } + } + } +} +signing { + sign publishing.publications.mavenJava +} \ No newline at end of file diff --git a/src/main/java/org/bdware/ypkdeploy/HTTPTool.java b/src/main/java/org/bdware/ypkdeploy/HTTPTool.java new file mode 100644 index 0000000..f810ffe --- /dev/null +++ b/src/main/java/org/bdware/ypkdeploy/HTTPTool.java @@ -0,0 +1,137 @@ +package org.bdware.ypkdeploy; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import org.apache.http.HttpEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.mime.MultipartEntityBuilder; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.bdware.client.ResultCallback; +import org.bouncycastle.crypto.CryptoException; +import org.bouncycastle.pqc.math.linearalgebra.ByteUtils; +import org.zz.gmhelper.SM2KeyPair; +import org.zz.gmhelper.SM2Util; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.atomic.AtomicInteger; + +public class HTTPTool { + final static String TAG = "[HTTPTool] "; + + static class DeployConfig { + //common config + String host; + String ypkPath; + String privateKey; + String publicKey; + String killBeforeStart; + JsonElement createParam; + //config for http deploy + int agentPort; + } + + + public static void batchRun(String path, boolean restart) { + DeployConfig config = null; + try { + config = new Gson().fromJson(new FileReader(path), DeployConfig.class); + } catch (Exception e) { + e.printStackTrace(); + System.out.println(TAG + " parse config failed!"); + } + deployUseHttp(config); + SM2KeyPair keyPair = SM2KeyPair.fromJson(new Gson().toJson(config)); + SmartContractClientExt ext = new SmartContractClientExt(String.format("ws://%s:%d/SCIDE/SCExecutor", config.host, config.agentPort), keyPair); + ext.waitForConnect(); + ext.login(); + for (; !ext.isLoggedIn; ) + Thread.yield(); + AtomicInteger counter = new AtomicInteger(0); + + try { + + if (config.killBeforeStart != null) + ext.kill(config.killBeforeStart, new ResultCallback() { + @Override + public void onResult(JsonObject r) { + System.out.println(TAG + r); + counter.incrementAndGet(); + } + }); + for (; counter.get() == 0; ) + Thread.yield(); + ; + Thread.sleep(200); + } catch (Exception e) { + e.printStackTrace(); + } + File f = new File(config.ypkPath); + ext.startContract(f.getName() + .replaceAll(".zip", "") + .replaceAll(".ypk", ""), config.createParam, new ResultCallback() { + @Override + public void onResult(JsonObject r) { + System.out.println(TAG + r); + counter.incrementAndGet(); + } + }); + for (; counter.get() == 1; ) + Thread.yield(); + ; + } + + + public static void deployUseHttp(DeployConfig config) { + String url = "http://%s:%d/Upload?%s&sign=%s"; + File file = new File(config.ypkPath); + String path = config.ypkPath; + String argWithoutSig = "path=%s&fileName=%s&isPrivate=true&order=%d&count=%d&pubKey=%s"; + SM2KeyPair keyPair = SM2KeyPair.fromJson(String.format("{\"publicKey\":\"%s\",\"privateKey\":\"%s\"}", config.publicKey, config.privateKey)); + String pubKey = keyPair.getPublicKeyStr(); + int trunc = 490 * 1024; + byte[] buff = new byte[trunc]; + int order = 0; + int count = (int) ((file.length() - 1) / (trunc)) + 1; + try { + FileInputStream fin = new FileInputStream(file); + for (int len = 0; (len = fin.read(buff)) > 0; order++) { + CloseableHttpClient client = HttpClients.createDefault(); + String arg = String.format(argWithoutSig, "./", file.getName(), order, count, pubKey); + String sign = ByteUtils.toHexString(SM2Util.sign(keyPair.getPrivateKeyParameter(), arg.getBytes(StandardCharsets.UTF_8))); + String urlStr = String.format(url, config.host, config.agentPort, arg, sign); + HttpPost httpPost = new HttpPost(urlStr); + MultipartEntityBuilder builder = MultipartEntityBuilder.create(); + if (len == trunc) + builder.addBinaryBody( + "file", buff, + ContentType.APPLICATION_OCTET_STREAM, file.getName()); + else { + byte[] bu = new byte[len]; + System.arraycopy(buff, 0, bu, 0, len); + builder.addBinaryBody( + "file", bu, + ContentType.APPLICATION_OCTET_STREAM, file.getName()); + + } + HttpEntity multipart = builder.build(); + httpPost.setEntity(multipart); + CloseableHttpResponse response = client.execute(httpPost); + ByteArrayOutputStream bo = new ByteArrayOutputStream(); + response.getEntity().writeTo(bo); + System.out.println("[YpkDeployTool] " + order + + " " + count + " " + bo); + } + } catch (IOException | CryptoException e) { + e.printStackTrace(); + } + } + + public static void main(String[] args) { + batchRun("./deployconfig.json", true); + } +} diff --git a/src/main/java/org/bdware/ypkdeploy/SmartContractClientExt.java b/src/main/java/org/bdware/ypkdeploy/SmartContractClientExt.java new file mode 100644 index 0000000..cde2092 --- /dev/null +++ b/src/main/java/org/bdware/ypkdeploy/SmartContractClientExt.java @@ -0,0 +1,308 @@ +package org.bdware.ypkdeploy; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bdware.client.ResultCallback; +import org.bdware.client.SmartContractClient; +import org.bdware.client.ws.Action; +import org.bouncycastle.crypto.CryptoException; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.pqc.math.linearalgebra.ByteUtils; +import org.zz.gmhelper.SM2KeyPair; +import org.zz.gmhelper.SM2Util; + +import java.security.SecureRandom; +import java.util.HashMap; +import java.util.Map; + +public class SmartContractClientExt extends SmartContractClient { + private static final Logger LOGGER = LogManager.getLogger(SmartContractClientExt.class); + public int count = 0; + public boolean isLoggedIn = false; + public String nodePubkey = null; + SecureRandom secureRandom = new SecureRandom(); + + public SmartContractClientExt(String uri, SM2KeyPair pair) { + super(uri, pair); + } + + public void startContract(String project) { + startContract(project, null, null); + } + + public void startContract(String project, JsonElement createParam) { + startContract(project, createParam, null); + } + + public void startContractByYpk(String path, boolean isPrivate, JsonElement createParam, ResultCallback rc) { + String reqID = System.currentTimeMillis() + ""; + Map ret = new HashMap<>(); + ret.put("action", "startContractByYPK"); + ret.put("isPrivate", isPrivate); + ret.put("owner", getKeyPair().getPublicKeyStr()); + ret.put("requestID", reqID); + ret.put("createParam", createParam); + ret.put("path", path); + String content = + String.format("Sole|%s|%s", ret.get("path"), getKeyPair().getPublicKeyStr()); + String sig; + try { + sig = + ByteUtils.toHexString( + SM2Util.sign( + getKeyPair().getPrivateKeyParameter(), content.getBytes())); + ret.put("signature", sig); + } catch (CryptoException e) { + e.printStackTrace(); + } + if (rc != null) { + cbs.put(reqID, rc); + } + this.sendMsg(new Gson().toJson(ret)); + } + + public void startPublicContract(String project, JsonElement createParam, ResultCallback rc) { + String reqID = System.currentTimeMillis() + ""; + Map ret = new HashMap<>(); + ret.put("action", "startContract"); + ret.put("isPrivate", false); + ret.put("owner", getKeyPair().getPublicKeyStr()); + ret.put("requestID", reqID); + ret.put("path", "/" + project + "/manifest.json"); + ret.put("createParam", createParam); + String content = + String.format("Sole|%s|%s", ret.get("path"), getKeyPair().getPublicKeyStr()); + String sig; + try { + sig = + ByteUtils.toHexString( + SM2Util.sign( + getKeyPair().getPrivateKeyParameter(), content.getBytes())); + ret.put("signature", sig); + } catch (CryptoException e) { + e.printStackTrace(); + } + + ret.put("script", "empty"); + if (rc != null) { + cbs.put(reqID, rc); + } + this.sendMsg(new Gson().toJson(ret)); + } + + public void startContract(String project, JsonElement createParam, ResultCallback rc) { + String reqID = System.currentTimeMillis() + ""; + Map ret = new HashMap<>(); + ret.put("action", "startContract"); + ret.put("isPrivate", true); + ret.put("owner", getKeyPair().getPublicKeyStr()); + ret.put("requestID", reqID); + ret.put("path", "/" + project + "/manifest.json"); + ret.put("createParam", createParam); + String content = + String.format("Sole|%s|%s", ret.get("path"), getKeyPair().getPublicKeyStr()); + String sig; + try { + sig = + ByteUtils.toHexString( + SM2Util.sign( + getKeyPair().getPrivateKeyParameter(), content.getBytes())); + ret.put("signature", sig); + } catch (CryptoException e) { + e.printStackTrace(); + } + + ret.put("script", "empty"); + if (rc != null) { + cbs.put(reqID, rc); + } + this.sendMsg(new Gson().toJson(ret)); + } + + @Override + public void onLogin(JsonObject obj) { + LOGGER.info(obj.toString()); + isLoggedIn = true; + } + + public void startMultiContractByScript(String script, String peersID, JsonElement createParam, ResultCallback rc) { + String reqID = System.currentTimeMillis() + "" + secureRandom.nextInt(); + Map ret = new HashMap<>(); + ret.put("action", "startContractMultiPoint"); + ret.put("script", script); + ret.put("type", 5); + ret.put("peersID", peersID); + ret.put("owner", getKeyPair().getPublicKeyStr()); + ret.put("requestID", reqID); + ret.put("createParam", createParam); + // "Algorithm|" + request.script + "|" + global.sm2Key.publicKey + ret.put("signature", doSignature(getKeyPair().getPrivateKeyParameter(), "Sole|" + script + "|" + ret.get("owner"))); + if (rc != null) { + cbs.put(reqID, rc); + } + String msg = new Gson().toJson(ret); + LOGGER.debug(msg); + this.sendMsg(msg); + } + + public String doSignature(ECPrivateKeyParameters privateKey, String content) { + try { + return + ByteUtils.toHexString( + SM2Util.sign( + privateKey, content.getBytes())); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + public static class ContractExecType { + public static final int Sole = 0; // 单点锚定合约 + public static final int RequestOnce = 1;// 多点不同步合约 + public static final int ResponseOnce = 2; // 多点不同步合约,超时的时候再请求一个, 直到获得一个response + public static final int RequestAllResponseFirst = 3; + public static final int RequestAllResponseHalf = 4; + public static final int RequestAllResponseAll = 5; + public static final int Sharding = 6; + public static final int SelfAdaptiveSharding = 7; + public static final int PBFT = 8; + + } + + public void startMultiContract( + boolean isPrivate, + String ypkName, + String peersID, + int contractExecType, + JsonElement createParam, + ResultCallback rc) { + String reqID = System.currentTimeMillis() + "" + secureRandom.nextInt(); + Map ret = new HashMap<>(); + ret.put("action", "startContractMultiPoint"); + ret.put("projectName", ypkName); + ret.put("isPrivate", isPrivate); + ret.put("type", contractExecType); + ret.put("peersID", peersID); + ret.put("owner", getKeyPair().getPublicKeyStr()); + ret.put("requestID", reqID); + ret.put("createParam", createParam); + // "Algorithm|" + request.script + "|" + global.sm2Key.publicKey + if (rc != null) { + cbs.put(reqID, rc); + } + String msg = new Gson().toJson(ret); + LOGGER.debug(msg); + this.sendMsg(msg); + } + + public void startContractByScript(String script, ResultCallback rc) { + String reqID = System.currentTimeMillis() + "" + Math.random(); + Map ret = new HashMap<>(); + ret.put("action", "startContract"); + ret.put("script", script); + ret.put("owner", getKeyPair().getPublicKeyStr()); + ret.put("requestID", reqID); + // "Algorithm|" + request.script + "|" + global.sm2Key.publicKey + ret.put( + "signature", + doSignature(getKeyPair().getPrivateKeyParameter(), "Algorithm|" + script + "|" + ret.get("owner"))); + if (rc != null) { + cbs.put(reqID, rc); + } + String msg = new Gson().toJson(ret); + LOGGER.debug(msg); + this.sendMsg(msg); + } + + public void kill(String name, ResultCallback rc) { + Map ret = new HashMap<>(); + ret.put("action", "killContractProcess"); + ret.put("verifiedPubKey", getKeyPair().getPublicKeyStr()); + ret.put("name", name); + String requestID = "kill_" + System.currentTimeMillis(); + ret.put("requestID", requestID); + if (rc != null) { + cbs.put(requestID, rc); + } + this.sendMsg(new Gson().toJson(ret)); + } + + public void killAllContractProcess() { + Map ret = new HashMap<>(); + ret.put("action", "killAllContract"); + this.sendMsg(new Gson().toJson(ret)); + } + + // public void onExecuteResult(JsonObject obj) { + // } + + public void genBDCoin(String name) { + Map ret = new HashMap<>(); + ret.put("action", "generateBDCoinProject"); + ret.put("ContractName", name); + ret.put("name", name); + } + + public void ping() { + this.sendMsg("{\"action\":\"ping\"}"); + } + + @Action + public void pong(JsonObject obj) { + count++; + } + + @Action + public void onStartContractTrustfullyResult(JsonObject jo) { + String responseID = jo.get("responseID").getAsString(); + ResultCallback cb = cbs.get(responseID); + if (cb != null) cb.onResult(jo); + } + + @Action + public void onKillContractProcess(JsonObject jo) { + if (!jo.has("responseID")) { + LOGGER.error("can't get responseID:" + jo.toString()); + return; + } + String responseID = jo.get("responseID").getAsString(); + ResultCallback cb = cbs.get(responseID); + if (cb != null) cb.onResult(jo); + } + + public void requestNodeInfo() { + this.sendMsg("{\"action\":\"loadNodeConfig\"}"); + } + + @Action + public void onLoadNodeConfig(JsonObject obj) { + JsonObject data = obj.get("data").getAsJsonObject(); + nodePubkey = data.get("nodePubKey").getAsString(); + } + + public void loginSync() { + login(); + for (; !isLoggedIn; ) + Thread.yield(); + } + + @Action + public void onQueryAEState(JsonObject obj) { + System.out.println("[AEState] " + obj.toString()); + } + + @Action + public void onQueryAECaller(JsonObject obj) { + System.out.println("[AECaller] " + obj.toString()); + } + + public void sendMsgTricky(String msg) { + this.sendMsg(msg); + } + + +} \ No newline at end of file