├── .gitignore ├── README.md ├── mvnw ├── mvnw.cmd ├── pom.xml └── src ├── main ├── java │ └── xyz │ │ └── huanxicloud │ │ └── blockchainj │ │ ├── BlockchainApplication.java │ │ ├── config │ │ ├── AppProperty.java │ │ └── SwaggerConfig.java │ │ ├── controller │ │ └── BlockController.java │ │ ├── core │ │ ├── Constants.java │ │ ├── blockchain │ │ │ ├── BlockChain.java │ │ │ ├── BlockService.java │ │ │ ├── block │ │ │ │ ├── Block.java │ │ │ │ ├── BlockBody.java │ │ │ │ ├── BlockHeader.java │ │ │ │ ├── MerkleTree.java │ │ │ │ └── instuction │ │ │ │ │ ├── Instruction.java │ │ │ │ │ ├── InstructionBase.java │ │ │ │ │ └── InstructionService.java │ │ │ └── db │ │ │ │ ├── DBConfig.java │ │ │ │ ├── DbStore.java │ │ │ │ └── LevelDbStoreImpl.java │ │ ├── common │ │ │ ├── encrypt │ │ │ │ ├── Sha256.java │ │ │ │ ├── TrustSDK.java │ │ │ │ ├── algorithm │ │ │ │ │ ├── AESAlgorithm.java │ │ │ │ │ ├── Base58Algorithm.java │ │ │ │ │ ├── BaseAlgorithm.java │ │ │ │ │ ├── DESAlgorithm.java │ │ │ │ │ └── ECDSAAlgorithm.java │ │ │ │ └── exception │ │ │ │ │ ├── ErrorNum.java │ │ │ │ │ └── TrustSDKException.java │ │ │ ├── returnmsg │ │ │ │ ├── ResultCode.java │ │ │ │ └── ReturnMessage.java │ │ │ ├── start │ │ │ │ └── AppStart.java │ │ │ └── util │ │ │ │ ├── CommonUtils.java │ │ │ │ └── FastJsonUtil.java │ │ ├── event │ │ │ ├── AddBlockEvent.java │ │ │ └── listener │ │ │ │ └── BlockGeneratedListener.java │ │ └── network │ │ │ ├── client │ │ │ ├── Instruction.java │ │ │ ├── NodeClient.java │ │ │ └── SocketClientManager.java │ │ │ └── server │ │ │ ├── SocketServerManager.java │ │ │ └── config │ │ │ └── WebSocketConfig.java │ │ └── data │ │ ├── dao │ │ ├── RoleMapper.java │ │ └── RoleMapping.java │ │ └── pojo │ │ └── Role.java └── resources │ ├── application.yml │ └── logback-spring.xml └── test └── java └── xyz └── huanxicloud └── blockchainj ├── BlockchainjApplicationTests.java └── Test.java /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | /nbproject/private/ 21 | /build/ 22 | /nbbuild/ 23 | /dist/ 24 | /nbdist/ 25 | /.nb-gradle/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # java实现区块链溯源系统 2 | > 关系型数据库操作太过复杂,希望可以模仿Hyledger Fabric使用非关系新数据库作为状态数据库 3 | > 4 | > 使用技术:spring,springboot,websocket,springmvc,mybatis,sqlite,levelDB, 5 | ## 开发目的 6 | > 实现分布式存诸,联盟角色共同维护一个数据库 7 | ## 区块链 8 | > 区块中包含指令,指令是对数据库操作称为HXSQL(对sql语句的封装) 9 | ## 智能合约 10 | > 解析并执行HXQL 11 | > 12 | > HXQL对象(执行操作,操作对象,操作类容) 13 | > 14 | > 存在问题:HXQL并不能完全解析成sql,只能转化成对应的ORM操作 15 | 16 | ## 共识机制 17 | 18 | >pbft 算法 19 | > 20 | >所有角色=3f+1(f为最大容错率) 21 | 22 | ## 存诸层 23 | 24 | > ​ 采用两个本地轻量级数据库分别存储,区块链和共同维护的数据库 25 | 26 | 1. levelDB(键值对存储) 27 | 28 | > 存储区块信息,以及最后一个区块的hash 29 | 30 | 2. sqlite(关系型数据库)//应用层数据区 31 | 32 | 1. 角色 (公钥(id),权限列表,状态,内容) 33 | 权限 34 | 商品(hash,描述(content),) 35 | 交易 36 | 37 | ## 网络传输 38 | 39 | > ​ 创建NodeSeedServer(节点种子服务器) 节点注册,监听节点变化,通知各节点,实现P2P(端对端,节点与每个节点建立websocket长连接实现网状模型)网络 40 | > 41 | > ​ 执行流程: 42 | > 43 | > 1. 节点携带局域网IP 连接种子服务器(websocket长连接),服务器记录IP 44 | > 45 | > 2. 种子服务器响应当前所有几点给连接节点 46 | > 47 | > 3. 连接节点与服务器返回的所有节点相连(与自身也相连) 48 | > 49 | > 4. 监听服务器几点变化 50 | > 51 | > TODO(未做): 52 | > 53 | > 客户端下线重连 54 | 55 | ## 商品流程(交易) 56 | 57 | 1. 生产 58 | 2. 检验 59 | 3. 出厂 --> 出现分支(商品与批次分离) 60 | 4. 销售 61 | 5. 运输 62 | 6. 使用 63 | ##用户查询商品交易信息(流程) 64 | 65 | ## 开发记录 66 | 67 | > 区块链就是将区块链起来保存在sqlite数据库中 68 | 通过公钥(数字身份证书)连接服务器获取权限信息 69 | 1.注册当前节点ip(方便广播) 广播有新节点加入 70 | 71 | 各节点运行 72 | -- 创世块内容绝对可信 73 | 有以下几个角色 74 | 75 | ## 联盟角色 76 | 77 | 1. CA(颁发证书) 78 | > 在创世块中注册ca,建初始化表 79 | > 80 | > CA权限,对 role 新增 (颁发数字身份证书),修改 81 | > 82 | > 角色注册,填写信息(申请证书->与ca产生交易记录区块指令中) 83 | 2. 厂家 84 | 发布产品(新增数据)-> output 商品ID 85 | 厂家送检测 -> 与检查机构产生一次交易 86 | 3. 检测机构 87 | (产生检测信息) 88 | 4. 商家 89 | 以上是节点 90 | 5. 用户 91 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Mingw, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | ########################################################################################## 204 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 205 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 206 | ########################################################################################## 207 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 208 | if [ "$MVNW_VERBOSE" = true ]; then 209 | echo "Found .mvn/wrapper/maven-wrapper.jar" 210 | fi 211 | else 212 | if [ "$MVNW_VERBOSE" = true ]; then 213 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 214 | fi 215 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" 216 | while IFS="=" read key value; do 217 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;; 218 | esac 219 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 220 | if [ "$MVNW_VERBOSE" = true ]; then 221 | echo "Downloading from: $jarUrl" 222 | fi 223 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 224 | 225 | if command -v wget > /dev/null; then 226 | if [ "$MVNW_VERBOSE" = true ]; then 227 | echo "Found wget ... using wget" 228 | fi 229 | wget "$jarUrl" -O "$wrapperJarPath" 230 | elif command -v curl > /dev/null; then 231 | if [ "$MVNW_VERBOSE" = true ]; then 232 | echo "Found curl ... using curl" 233 | fi 234 | curl -o "$wrapperJarPath" "$jarUrl" 235 | else 236 | if [ "$MVNW_VERBOSE" = true ]; then 237 | echo "Falling back to using Java to download" 238 | fi 239 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 240 | if [ -e "$javaClass" ]; then 241 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 242 | if [ "$MVNW_VERBOSE" = true ]; then 243 | echo " - Compiling MavenWrapperDownloader.java ..." 244 | fi 245 | # Compiling the Java class 246 | ("$JAVA_HOME/bin/javac" "$javaClass") 247 | fi 248 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 249 | # Running the downloader 250 | if [ "$MVNW_VERBOSE" = true ]; then 251 | echo " - Running MavenWrapperDownloader.java ..." 252 | fi 253 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 254 | fi 255 | fi 256 | fi 257 | fi 258 | ########################################################################################## 259 | # End of extension 260 | ########################################################################################## 261 | 262 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 263 | if [ "$MVNW_VERBOSE" = true ]; then 264 | echo $MAVEN_PROJECTBASEDIR 265 | fi 266 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 267 | 268 | # For Cygwin, switch paths to Windows format before running java 269 | if $cygwin; then 270 | [ -n "$M2_HOME" ] && 271 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 272 | [ -n "$JAVA_HOME" ] && 273 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 274 | [ -n "$CLASSPATH" ] && 275 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 276 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 277 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 278 | fi 279 | 280 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 281 | 282 | exec "$JAVACMD" \ 283 | $MAVEN_OPTS \ 284 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 285 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 286 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 287 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" 124 | FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( 125 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 126 | ) 127 | 128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 130 | if exist %WRAPPER_JAR% ( 131 | echo Found %WRAPPER_JAR% 132 | ) else ( 133 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 134 | echo Downloading from: %DOWNLOAD_URL% 135 | powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" 136 | echo Finished downloading %WRAPPER_JAR% 137 | ) 138 | @REM End of extension 139 | 140 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 141 | if ERRORLEVEL 1 goto error 142 | goto end 143 | 144 | :error 145 | set ERROR_CODE=1 146 | 147 | :end 148 | @endlocal & set ERROR_CODE=%ERROR_CODE% 149 | 150 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 151 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 152 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 153 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 154 | :skipRcPost 155 | 156 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 157 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 158 | 159 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 160 | 161 | exit /B %ERROR_CODE% 162 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.1.1.RELEASE 9 | 10 | 11 | 12 | xyz.huanxicloud 13 | blockchainj 14 | 0.0.1-SNAPSHOT 15 | blockchainj 16 | blockchain for java 17 | 18 | 1.8 19 | 20 | 21 | 22 | ali-repos 23 | ali Repository 24 | http://maven.aliyun.com/nexus/content/groups/public 25 | 26 | 27 | center-repos 28 | center Repository 29 | http://repo1.maven.org/maven2 30 | 31 | 32 | 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-starter-websocket 37 | 38 | 39 | 40 | com.squareup.okhttp3 41 | okhttp 42 | 3.12.1 43 | 44 | 45 | 46 | org.mybatis.spring.boot 47 | mybatis-spring-boot-starter 48 | 1.3.2 49 | 50 | 51 | 52 | com.alibaba 53 | druid 54 | 1.1.9 55 | 56 | 57 | 58 | org.xerial 59 | sqlite-jdbc 60 | 3.20.0 61 | 62 | 63 | 64 | io.springfox 65 | springfox-swagger2 66 | 2.2.2 67 | 68 | 69 | io.springfox 70 | springfox-swagger-ui 71 | 2.2.2 72 | 73 | 74 | 75 | com.google.guava 76 | guava 77 | 26.0-jre 78 | 79 | 80 | 81 | com.github.xiaoymin 82 | swagger-bootstrap-ui 83 | 1.7 84 | 85 | 86 | 87 | com.madgag.spongycastle 88 | core 89 | 1.54.0.0 90 | 91 | 92 | org.bouncycastle 93 | bcprov-jdk15on 94 | 1.55 95 | 96 | 97 | commons-codec 98 | commons-codec 99 | 1.10 100 | 101 | 102 | 103 | org.springframework.boot 104 | spring-boot-configuration-processor 105 | true 106 | 107 | 108 | 109 | org.iq80.leveldb 110 | leveldb 111 | 0.10 112 | 113 | 114 | org.iq80.leveldb 115 | leveldb-api 116 | 0.10 117 | 118 | 119 | 120 | cn.hutool 121 | hutool-all 122 | 4.0.5 123 | 124 | 125 | com.alibaba 126 | fastjson 127 | 1.2.29 128 | 129 | 130 | 131 | org.springframework.boot 132 | spring-boot-starter-web 133 | 2.0.0.RELEASE 134 | 135 | 136 | 137 | org.springframework.boot 138 | spring-boot-devtools 139 | runtime 140 | 141 | 142 | org.springframework.boot 143 | spring-boot-starter-test 144 | test 145 | 146 | 147 | 148 | 149 | 150 | 151 | org.springframework.boot 152 | spring-boot-maven-plugin 153 | 154 | 155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/BlockchainApplication.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj; 2 | 3 | import org.mybatis.spring.annotation.MapperScan; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | 7 | @SpringBootApplication 8 | public class BlockchainApplication { 9 | 10 | public static void main(String[] args) { 11 | SpringApplication.run(BlockchainApplication.class, args); 12 | } 13 | 14 | } 15 | 16 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/config/AppProperty.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.config; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | import org.springframework.stereotype.Component; 5 | 6 | /** 7 | * @author: huanxi 8 | * @date: 2019/1/4 18:51 9 | * 应用启动配置 10 | */ 11 | @Component 12 | @ConfigurationProperties(prefix = "app") 13 | public class AppProperty { 14 | private String publicKey; 15 | private String privateKey; 16 | private String myIp; //当前节点IP 17 | private String nodeServer;//节点种子服务器 18 | 19 | public String getMyIp() { 20 | return myIp; 21 | } 22 | 23 | public void setMyIp(String myIp) { 24 | this.myIp = myIp; 25 | } 26 | 27 | public String getNodeServer() { 28 | return nodeServer; 29 | } 30 | 31 | public void setNodeServer(String nodeServer) { 32 | this.nodeServer = nodeServer; 33 | } 34 | 35 | public String getPublicKey() { 36 | return publicKey; 37 | } 38 | 39 | public void setPublicKey(String publicKey) { 40 | this.publicKey = publicKey; 41 | } 42 | 43 | public String getPrivateKey() { 44 | return privateKey; 45 | } 46 | 47 | public void setPrivateKey(String privateKey) { 48 | this.privateKey = privateKey; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/config/SwaggerConfig.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 6 | import springfox.documentation.builders.ApiInfoBuilder; 7 | import springfox.documentation.builders.PathSelectors; 8 | import springfox.documentation.builders.RequestHandlerSelectors; 9 | import springfox.documentation.service.ApiInfo; 10 | import springfox.documentation.spi.DocumentationType; 11 | import springfox.documentation.spring.web.plugins.Docket; 12 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 13 | /** 14 | * swagger配置 15 | */ 16 | @Configuration 17 | @EnableSwagger2 18 | public class SwaggerConfig { 19 | @Bean 20 | public Docket createRestApi() { 21 | return new Docket(DocumentationType.SWAGGER_2) 22 | .apiInfo(apiInfo()) 23 | .select() 24 | .apis(RequestHandlerSelectors.basePackage("xyz.huanxicloud.blockchainj.controller")) 25 | .paths(PathSelectors.any()) 26 | .build(); 27 | } 28 | private ApiInfo apiInfo() { 29 | return new ApiInfoBuilder() 30 | .title("Spring Boot中使用Swagger2构建RESTful APIs") 31 | .description("更多请关注http://www.baidu.com") 32 | .termsOfServiceUrl("http://www.baidu.com") 33 | .contact("huanxi") 34 | .version("1.0") 35 | .build(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/controller/BlockController.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.controller; 2 | 3 | import io.swagger.annotations.Api; 4 | import io.swagger.annotations.ApiOperation; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.context.ApplicationContext; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.PostMapping; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RestController; 11 | import xyz.huanxicloud.blockchainj.core.blockchain.BlockChain; 12 | import xyz.huanxicloud.blockchainj.core.common.returnmsg.ReturnMessage; 13 | import xyz.huanxicloud.blockchainj.core.event.AddBlockEvent; 14 | import xyz.huanxicloud.blockchainj.data.dao.RoleMapper; 15 | 16 | import javax.annotation.Resource; 17 | 18 | /** 19 | * @author: huanxi 20 | * @date: 2019/1/4 19:31 21 | */ 22 | @Api(tags = "区块接口") 23 | @RestController 24 | @RequestMapping("/block") 25 | public class BlockController { 26 | @Resource 27 | private BlockChain blockChain; 28 | @Autowired 29 | RoleMapper roleMapper; 30 | @Autowired 31 | ApplicationContext publisher; 32 | 33 | @GetMapping("test") 34 | @ApiOperation(value = "测试api") 35 | public void test() { 36 | publisher.publishEvent(new AddBlockEvent(blockChain.getLastBlock())); 37 | // roleMapper.test(); 38 | } 39 | 40 | @GetMapping("last") 41 | @ApiOperation(value = "查看最后一个区块") 42 | public ReturnMessage getLast() { 43 | return new ReturnMessage(1, blockChain.getLastBlock()); 44 | } 45 | 46 | @PostMapping("create") 47 | @ApiOperation(value = "创建一个交易") 48 | public ReturnMessage createBlock() { 49 | return new ReturnMessage(1, blockChain.getLastBlock()); 50 | } 51 | 52 | @GetMapping("all") 53 | @ApiOperation(value = "查看所有区块") 54 | public ReturnMessage getAll() { 55 | // return new ReturnMessage(1, blockChain.getAllBlock()); 56 | return null; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/Constants.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.core; 2 | 3 | import java.math.BigInteger; 4 | 5 | /** 6 | * @author: huanxi 7 | * @date: 2019/1/4 17:22 8 | */ 9 | public class Constants { 10 | public final static int Version = 1; 11 | public static final String RANDOM_NUMBER_ALGORITHM_PROVIDER = "SUN"; 12 | public static final String RANDOM_NUMBER_ALGORITHM = "SHA1PRNG"; 13 | public static final BigInteger MAXPRIVATEKEY = new BigInteger("00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140", 16); 14 | public static String blockChainPath = "./data/blockchain"; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/blockchain/BlockChain.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.core.blockchain; 2 | 3 | import cn.hutool.core.collection.CollectionUtil; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.stereotype.Service; 7 | import org.springframework.util.StringUtils; 8 | import xyz.huanxicloud.blockchainj.core.blockchain.block.Block; 9 | import xyz.huanxicloud.blockchainj.core.blockchain.block.instuction.InstructionService; 10 | import xyz.huanxicloud.blockchainj.core.blockchain.db.DbStore; 11 | import xyz.huanxicloud.blockchainj.core.common.util.FastJsonUtil; 12 | 13 | import javax.annotation.PostConstruct; 14 | import javax.annotation.Resource; 15 | 16 | /** 17 | * @author: huanxi 18 | * @date: 2019/1/4 17:48 19 | * 区块链 服务 20 | */ 21 | @Service 22 | public class BlockChain { 23 | @Resource 24 | private DbStore dbStore; 25 | @Resource 26 | private BlockService blockService; 27 | @Resource 28 | InstructionService instructionService; 29 | private final String lastBlockHashKey = "LastHash"; 30 | private Logger logger = LoggerFactory.getLogger(getClass()); 31 | private String lastBlockHash; 32 | 33 | /** 34 | * 创建创世块 35 | */ 36 | private Block createGenesisBlock() { 37 | //构建区块体 38 | return blockService.createBlock(CollectionUtil.newArrayList(instructionService.build("创世块"))); 39 | } 40 | 41 | /** 42 | * 本地初始化区块链 43 | */ 44 | @PostConstruct 45 | public void init() { 46 | //获取last hash 如果没有则创建创世块 47 | lastBlockHash = dbStore.get(lastBlockHashKey); 48 | if (StringUtils.isEmpty(lastBlockHash)) addBlock(createGenesisBlock()); 49 | } 50 | 51 | //添加新区块 52 | public void addBlock(Block block) { 53 | lastBlockHash = block.getHash(); 54 | dbStore.put(lastBlockHashKey, lastBlockHash); 55 | dbStore.put(lastBlockHash, FastJsonUtil.toJSONString(block)); 56 | } 57 | 58 | //获取最后一个区块 59 | public Block getLastBlock() { 60 | return getBlockByHash(lastBlockHash); 61 | } 62 | 63 | public Block getBlockByHash(String hash) { 64 | String blockJson = dbStore.get(hash); 65 | return FastJsonUtil.toBean(blockJson, Block.class); 66 | } 67 | 68 | public String getLastBlockHash() { 69 | return lastBlockHash; 70 | } 71 | 72 | public void setLastBlockHash(String lastBlockHash) { 73 | this.lastBlockHash = lastBlockHash; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/blockchain/BlockService.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.core.blockchain; 2 | 3 | import org.springframework.stereotype.Service; 4 | import xyz.huanxicloud.blockchainj.core.Constants; 5 | import xyz.huanxicloud.blockchainj.core.blockchain.block.*; 6 | import xyz.huanxicloud.blockchainj.core.blockchain.block.instuction.Instruction; 7 | import xyz.huanxicloud.blockchainj.config.AppProperty; 8 | import xyz.huanxicloud.blockchainj.core.common.util.CommonUtils; 9 | 10 | import javax.annotation.Resource; 11 | import java.util.List; 12 | import java.util.stream.Collectors; 13 | 14 | /** 15 | * @author: huanxi 16 | * @date: 2019/1/4 17:16 17 | * 区块服务 18 | */ 19 | @Service 20 | public class BlockService { 21 | @Resource 22 | AppProperty appProperty; 23 | @Resource 24 | BlockChain blockChain; 25 | /** 26 | * 27 | * 生成新区块(生成者) 28 | */ 29 | public Block createBlock(List instructions) { 30 | //创建区块头 31 | BlockHeader blockHeader = new BlockHeader(); 32 | blockHeader.setVersion(Constants.Version); 33 | blockHeader.setTimeStamp(CommonUtils.getTimestamp()); 34 | //计算hash merkle 35 | List hashList = instructions.stream().map(Instruction::getHash).collect(Collectors 36 | .toList()); //TODO merkle tree 37 | blockHeader.setHashMerkleRoot(new MerkleTree(hashList).build().getRoot()); 38 | blockHeader.setNonce(0); //测试 39 | blockHeader.setPublicKey(appProperty.getPublicKey()); 40 | blockHeader.setHashPreviousBlock(blockChain.getLastBlockHash()); 41 | return new Block(blockHeader, instructions); 42 | } 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/blockchain/block/Block.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.core.blockchain.block; 2 | 3 | import xyz.huanxicloud.blockchainj.core.blockchain.block.instuction.Instruction; 4 | import xyz.huanxicloud.blockchainj.core.common.encrypt.Sha256; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * @author: huanxi 10 | * @date: 2019/1/1 17:57 11 | * 区块结构 12 | */ 13 | public class Block { 14 | /** 15 | * 区块头 16 | */ 17 | private BlockHeader blockHeader; 18 | /** 19 | * 区块body 20 | */ 21 | private BlockBody blockBody; 22 | /** 23 | * 该区块的hash 24 | */ 25 | private String hash; 26 | 27 | public Block(BlockHeader blockHeader, BlockBody blockBody) { 28 | this.blockHeader = blockHeader; 29 | this.blockBody = blockBody; 30 | this.setHash(); 31 | } 32 | 33 | public Block(BlockHeader blockHeader, List instructions) { 34 | BlockBody blockBody = new BlockBody(); 35 | blockBody.setInstructions(instructions); 36 | this.blockHeader = blockHeader; 37 | this.blockBody = blockBody; 38 | this.setHash(); 39 | } 40 | 41 | public Block() { 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return "Block{" + 47 | "blockHeader=" + blockHeader + 48 | ", blockBody=" + blockBody + 49 | ", hash='" + hash + '\'' + 50 | '}'; 51 | } 52 | 53 | public BlockHeader getBlockHeader() { 54 | return blockHeader; 55 | } 56 | 57 | public void setBlockHeader(BlockHeader blockHeader) { 58 | this.blockHeader = blockHeader; 59 | } 60 | 61 | public BlockBody getBlockBody() { 62 | return blockBody; 63 | } 64 | 65 | public void setBlockBody(BlockBody blockBody) { 66 | this.blockBody = blockBody; 67 | } 68 | 69 | public String getHash() { 70 | return hash; 71 | } 72 | 73 | public void setHash() { 74 | this.hash = Sha256.sum(blockHeader.toString() + blockBody.toString()); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/blockchain/block/BlockBody.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.core.blockchain.block; 2 | 3 | import xyz.huanxicloud.blockchainj.core.blockchain.block.instuction.Instruction; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @author: huanxi 9 | * @date: 2019/1/1 17:57 10 | * 区块体 11 | */ 12 | public class BlockBody { 13 | private List instructions; 14 | 15 | @Override 16 | public String toString() { 17 | return "BlockBody{" + 18 | "instructions=" + instructions + 19 | '}'; 20 | } 21 | 22 | public List getInstructions() { 23 | return instructions; 24 | } 25 | 26 | public void setInstructions(List instructions) { 27 | this.instructions = instructions; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/blockchain/block/BlockHeader.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.core.blockchain.block; 2 | 3 | /** 4 | * @author: huanxi 5 | * @date: 2019/1/1 17:57 6 | * 区块头结构 7 | */ 8 | public class BlockHeader { 9 | /** 10 | * 版本号 11 | */ 12 | private int version; 13 | /** 14 | * 上一区块的hash 15 | */ 16 | private String hashPreviousBlock; 17 | /** 18 | * merkle tree根节点hash 19 | */ 20 | private String hashMerkleRoot; 21 | /** 22 | * 生成该区块的公钥 23 | */ 24 | private String publicKey; 25 | /** 26 | * 时间戳 27 | */ 28 | private long timeStamp; 29 | /** 30 | * 32位随机数 31 | */ 32 | private long nonce; 33 | 34 | /** 35 | * 该区块里每条交易信息的hash集合,按顺序来的,通过该hash集合能算出根节点hash 36 | */ 37 | @Override 38 | public String toString() { 39 | return "BlockHeader{" + 40 | "version=" + version + 41 | ", hashPreviousBlock='" + hashPreviousBlock + '\'' + 42 | ", hashMerkleRoot='" + hashMerkleRoot + '\'' + 43 | ", publicKey='" + publicKey + '\'' + 44 | ", timeStamp=" + timeStamp + 45 | ", nonce=" + nonce + 46 | '}'; 47 | } 48 | 49 | public int getVersion() { 50 | return version; 51 | } 52 | 53 | public void setVersion(int version) { 54 | this.version = version; 55 | } 56 | 57 | public String getHashPreviousBlock() { 58 | return hashPreviousBlock; 59 | } 60 | 61 | public void setHashPreviousBlock(String hashPreviousBlock) { 62 | this.hashPreviousBlock = hashPreviousBlock; 63 | } 64 | 65 | public String getHashMerkleRoot() { 66 | return hashMerkleRoot; 67 | } 68 | 69 | public void setHashMerkleRoot(String hashMerkleRoot) { 70 | this.hashMerkleRoot = hashMerkleRoot; 71 | } 72 | 73 | public String getPublicKey() { 74 | return publicKey; 75 | } 76 | 77 | public void setPublicKey(String publicKey) { 78 | this.publicKey = publicKey; 79 | } 80 | 81 | 82 | public long getTimeStamp() { 83 | return timeStamp; 84 | } 85 | 86 | public void setTimeStamp(long timeStamp) { 87 | this.timeStamp = timeStamp; 88 | } 89 | 90 | public long getNonce() { 91 | return nonce; 92 | } 93 | 94 | public void setNonce(long nonce) { 95 | this.nonce = nonce; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/blockchain/block/MerkleTree.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.core.blockchain.block; 2 | 3 | import cn.hutool.crypto.digest.DigestUtil; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | /** 9 | * merkle tree简单实现 10 | * 11 | */ 12 | public class MerkleTree { 13 | /** 14 | * transaction List 15 | */ 16 | private List txList; 17 | /** 18 | * Merkle Root 19 | */ 20 | private String root; 21 | 22 | /** 23 | * constructor 24 | * 25 | * @param txList 26 | * transaction List 交易List 27 | */ 28 | public MerkleTree(List txList) { 29 | this.txList = txList; 30 | root = ""; 31 | } 32 | 33 | /** 34 | * execute merkle_tree and set root. 35 | */ 36 | public MerkleTree build() { 37 | List tempTxList = new ArrayList<>(this.txList); 38 | 39 | List newTxList = getNewTxList(tempTxList); 40 | 41 | while (newTxList.size() != 1) { 42 | newTxList = getNewTxList(newTxList); 43 | } 44 | 45 | this.root = newTxList.get(0); 46 | return this; 47 | } 48 | 49 | /** 50 | * return Node Hash List. 51 | * 52 | * @param tempTxList 53 | * list 54 | * @return 55 | * 某一层的左右节点相连hash 56 | */ 57 | private List getNewTxList(List tempTxList) { 58 | List newTxList = new ArrayList<>(); 59 | int index = 0; 60 | while (index < tempTxList.size()) { 61 | // left 62 | String left = tempTxList.get(index); 63 | index++; 64 | // right 65 | String right = ""; 66 | if (index != tempTxList.size()) { 67 | right = tempTxList.get(index); 68 | } 69 | // sha2 hex value 70 | String sha2HexValue = DigestUtil.sha256Hex(left + right); 71 | newTxList.add(sha2HexValue); 72 | index++; 73 | } 74 | 75 | return newTxList; 76 | } 77 | 78 | /** 79 | * Get Root 80 | * 81 | * @return 82 | * 根节点hash 83 | */ 84 | public String getRoot() { 85 | return this.root; 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/blockchain/block/instuction/Instruction.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.core.blockchain.block.instuction; 2 | 3 | import xyz.huanxicloud.blockchainj.core.common.encrypt.Sha256; 4 | 5 | /** 6 | * @author: huanxi 7 | * @date: 2019/1/1 8 | * 区块body内一条指令 9 | */ 10 | public class Instruction extends InstructionBase { 11 | /** 12 | * 指令的内容 13 | */ 14 | private String content; 15 | /** 16 | * 时间戳 17 | */ 18 | private Long timeStamp; 19 | /** 20 | * 操作人的公钥 21 | */ 22 | private String inputKey; 23 | /** 24 | * 指定人的公钥 25 | */ 26 | private String outputKey; 27 | /** 28 | * 操作人签名 29 | */ 30 | private String sign; 31 | 32 | public String getContent() { 33 | return content; 34 | } 35 | 36 | public void setContent(String content) { 37 | this.content = content; 38 | } 39 | 40 | public Long getTimeStamp() { 41 | return timeStamp; 42 | } 43 | 44 | public void setTimeStamp(Long timeStamp) { 45 | this.timeStamp = timeStamp; 46 | } 47 | 48 | public String getInputKey() { 49 | return inputKey; 50 | } 51 | 52 | public void setInputKey(String inputKey) { 53 | this.inputKey = inputKey; 54 | } 55 | 56 | public String getSign() { 57 | return sign; 58 | } 59 | 60 | public void setSign(String sign) { 61 | this.sign = sign; 62 | } 63 | 64 | public String getHash() { 65 | return Sha256.sum(toString()); 66 | } 67 | 68 | 69 | @Override 70 | public String toString() { 71 | return "Instruction{" + 72 | "content='" + content + '\'' + 73 | ", timeStamp=" + timeStamp + 74 | ", input='" + inputKey + '\'' + 75 | ", sign='" + sign + '\'' + 76 | ", output='" + outputKey+ '\'' + 77 | '}'; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/blockchain/block/instuction/InstructionBase.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.core.blockchain.block.instuction; 2 | 3 | /** 4 | * blockBody内一条指令的基础属性 5 | */ 6 | public class InstructionBase { 7 | /** 8 | * 指令的操作,增删改(1,-1,2) 9 | */ 10 | private byte operation; 11 | /** 12 | * 操作的表名 13 | */ 14 | private String table; 15 | /** 16 | * 最终要执行入库的json内容 17 | */ 18 | private String oldJson; 19 | /** 20 | * 业务id,sql语句中where需要该Id 21 | */ 22 | private String instructionId; 23 | 24 | public byte getOperation() { 25 | return operation; 26 | } 27 | 28 | public void setOperation(byte operation) { 29 | this.operation = operation; 30 | } 31 | 32 | public String getTable() { 33 | return table; 34 | } 35 | 36 | public void setTable(String table) { 37 | this.table = table; 38 | } 39 | 40 | public String getOldJson() { 41 | return oldJson; 42 | } 43 | 44 | public void setOldJson(String oldJson) { 45 | this.oldJson = oldJson; 46 | } 47 | 48 | public String getInstructionId() { 49 | return instructionId; 50 | } 51 | 52 | public void setInstructionId(String instructionId) { 53 | this.instructionId = instructionId; 54 | } 55 | 56 | @Override 57 | public String toString() { 58 | return "InstructionReverse{" + 59 | "operation=" + operation + 60 | ", table='" + table + '\'' + 61 | ", oldJson='" + oldJson + '\'' + 62 | ", instructionId='" + instructionId + '\'' + 63 | '}'; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/blockchain/block/instuction/InstructionService.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.core.blockchain.block.instuction; 2 | 3 | import org.springframework.stereotype.Service; 4 | import xyz.huanxicloud.blockchainj.config.AppProperty; 5 | import xyz.huanxicloud.blockchainj.core.common.encrypt.TrustSDK; 6 | import xyz.huanxicloud.blockchainj.core.common.encrypt.exception.TrustSDKException; 7 | import xyz.huanxicloud.blockchainj.core.common.util.CommonUtils; 8 | 9 | import javax.annotation.Resource; 10 | import java.io.UnsupportedEncodingException; 11 | 12 | /** 13 | * @author: huanxi 14 | * @date: 2019/1/4 18:59 15 | */ 16 | @Service 17 | public class InstructionService { 18 | @Resource 19 | AppProperty appProperty; 20 | 21 | public Instruction build(String json) { 22 | Instruction instruction = new Instruction(); 23 | instruction.setContent(json); 24 | instruction.setInputKey(appProperty.getPublicKey()); 25 | instruction.setTimeStamp(CommonUtils.getTimestamp()); 26 | //用私钥签名(指令hash)让供别人验证 27 | try { 28 | instruction.setSign(TrustSDK.signString(appProperty.getPrivateKey(), instruction.getHash())); 29 | } catch (TrustSDKException | UnsupportedEncodingException e) { 30 | e.printStackTrace(); 31 | } 32 | return instruction; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/blockchain/db/DBConfig.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.core.blockchain.db; 2 | 3 | import org.iq80.leveldb.DB; 4 | import org.iq80.leveldb.impl.Iq80DBFactory; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import xyz.huanxicloud.blockchainj.core.Constants; 8 | 9 | import java.io.File; 10 | import java.io.IOException; 11 | 12 | /** 13 | * @author: huanxi 14 | * @date: 2019/1/4 17:53 15 | * 自动配置levelDB 16 | */ 17 | @Configuration 18 | public class DBConfig { 19 | @Bean 20 | public DB levelDB() throws IOException { 21 | org.iq80.leveldb.Options options = new org.iq80.leveldb.Options(); 22 | options.createIfMissing(true); 23 | return Iq80DBFactory.factory.open(new File(Constants.blockChainPath), options); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/blockchain/db/DbStore.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.core.blockchain.db; 2 | 3 | /** 4 | * key-value型DB数据库操作接口 5 | */ 6 | public interface DbStore { 7 | /** 8 | * 数据库key value 9 | * 10 | * @param key key 11 | * @param value value 12 | */ 13 | void put(String key, String value); 14 | 15 | /** 16 | * get By Key 17 | * 18 | * @param key key 19 | * @return value 20 | */ 21 | String get(String key); 22 | 23 | /** 24 | * remove by key 25 | * 26 | * @param key key 27 | */ 28 | void remove(String key); 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/blockchain/db/LevelDbStoreImpl.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.core.blockchain.db; 2 | 3 | import org.iq80.leveldb.DB; 4 | import org.springframework.stereotype.Component; 5 | import org.springframework.stereotype.Service; 6 | 7 | import javax.annotation.Resource; 8 | 9 | import static org.iq80.leveldb.impl.Iq80DBFactory.asString; 10 | import static org.iq80.leveldb.impl.Iq80DBFactory.bytes; 11 | 12 | /** 13 | * levelDB 存储键值对 14 | */ 15 | @Component 16 | public class LevelDbStoreImpl implements DbStore { 17 | @Resource 18 | private DB db; 19 | 20 | @Override 21 | public void put(String key, String value) { 22 | db.put(bytes(key), bytes(value)); 23 | } 24 | 25 | @Override 26 | public String get(String key) { 27 | return asString(db.get(bytes(key))); 28 | } 29 | 30 | @Override 31 | public void remove(String key) { 32 | db.delete(bytes(key)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/common/encrypt/Sha256.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.core.common.encrypt; 2 | 3 | 4 | import cn.hutool.crypto.digest.DigestUtil; 5 | 6 | /** 7 | * @author: huanxi 8 | * @date: 2019/1/1 20:28 9 | */ 10 | public class Sha256 { 11 | public static String sum(String input) { 12 | return DigestUtil.sha256Hex(input); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/common/encrypt/TrustSDK.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Project Name:trustsql_sdk 3 | * File Name:TrustSDK.java 4 | * Package Name:com.tencent.trustsql.sdk 5 | * Date:Jul 26, 201710:30:31 AM 6 | * Copyright (c) 2017, Tencent All Rights Reserved. 7 | */ 8 | 9 | package xyz.huanxicloud.blockchainj.core.common.encrypt; 10 | 11 | import org.apache.tomcat.util.codec.binary.Base64; 12 | import org.springframework.util.StringUtils; 13 | import xyz.huanxicloud.blockchainj.core.common.encrypt.algorithm.ECDSAAlgorithm; 14 | import xyz.huanxicloud.blockchainj.core.common.encrypt.exception.ErrorNum; 15 | import xyz.huanxicloud.blockchainj.core.common.encrypt.exception.TrustSDKException; 16 | 17 | import java.io.UnsupportedEncodingException; 18 | 19 | /** 20 | * ClassName:TrustSDK
21 | * Date: Jul 26, 2017 10:30:31 AM
22 | * 23 | * @author Rony 24 | * @since JDK 1.7 25 | */ 26 | public class TrustSDK { 27 | 28 | /** 29 | * generatePairKey:产生一对公私钥, 并返回.
30 | * 31 | * @author Rony 32 | * @return 返回公私钥对 33 | * @throws TrustSDKException 34 | * TrustSDKException 35 | * @since JDK 1.7 36 | */ 37 | /* public static PairKey generatePairKey() throws TrustSDKException { 38 | return generatePairKey(false); 39 | }*/ 40 | 41 | /** 42 | * generatePairKey:生成私钥公钥对.
43 | * 44 | * @author ronyyang 45 | * @param encodePubKey 是否压缩 46 | * @return PairKey 47 | * @throws TrustSDKException 48 | * TrustSDKException 49 | * @since JDK 1.7 50 | */ 51 | /* public static PairKey generatePairKey(boolean encodePubKey) throws TrustSDKException { 52 | try { 53 | PairKey pair = new PairKey(); 54 | String privateKey = ECDSAAlgorithm.generatePrivateKey(); 55 | String pubKey = ECDSAAlgorithm.generatePublicKey(privateKey.trim(), encodePubKey); 56 | pair.setPrivateKey(privateKey); 57 | pair.setInputKey(pubKey); 58 | return pair; 59 | } catch (Exception e) { 60 | throw new TrustSDKException(ErrorNum.ECDSA_ENCRYPT_ERROR.getRetCode(), ErrorNum.ECDSA_ENCRYPT_ERROR.getRetMsg(), e); 61 | } 62 | }*/ 63 | 64 | /** 65 | * checkPairKey:验证一对公私钥是否匹配.
66 | * 67 | * @author ronyyang 68 | * @param prvKey 输入 存放私钥 长度必须为PRVKEY_DIGEST_LENGTH 69 | * @param pubKey 输入 存放公钥 长度必须为PUBKEY_DIGEST_LENGTH 70 | * @return true 公私钥匹配 false 公私钥不匹配 71 | * @throws TrustSDKException TrustSDKException 72 | * @since JDK 1.7 73 | */ 74 | public static boolean checkPairKey(String prvKey, String pubKey) throws TrustSDKException { 75 | if (StringUtils.isEmpty(prvKey) || StringUtils.isEmpty(pubKey)) { 76 | throw new TrustSDKException(ErrorNum.INVALID_PARAM_ERROR.getRetCode(), ErrorNum.INVALID_PARAM_ERROR.getRetMsg()); 77 | } 78 | try { 79 | String correctPubKey = ECDSAAlgorithm.generatePublicKey(prvKey.trim(), true); 80 | return pubKey.trim().equals(correctPubKey); 81 | } catch (Exception e) { 82 | throw new TrustSDKException(ErrorNum.ECDSA_ENCRYPT_ERROR.getRetCode(), ErrorNum.ECDSA_ENCRYPT_ERROR.getRetMsg(), e); 83 | } 84 | } 85 | 86 | /** 87 | * generatePubkeyByPrvkey: 通过私钥计算相应公钥.
88 | * 89 | * @author Rony 90 | * @param privateKey 91 | * 私钥字符串 92 | * @param encode 93 | * 是否压缩公钥 94 | * @return 返回公钥字符串 95 | * @throws TrustSDKException 96 | * TrustSDKException 97 | * @since JDK 1.7 98 | */ 99 | public static String generatePubkeyByPrvkey(String privateKey, boolean encode) throws TrustSDKException { 100 | if (StringUtils.isEmpty(privateKey)) { 101 | throw new TrustSDKException(ErrorNum.INVALID_PARAM_ERROR.getRetCode(), ErrorNum.INVALID_PARAM_ERROR.getRetMsg()); 102 | } 103 | try { 104 | return ECDSAAlgorithm.generatePublicKey(privateKey, encode); 105 | } catch (Exception e) { 106 | throw new TrustSDKException(ErrorNum.ECDSA_ENCRYPT_ERROR.getRetCode(), ErrorNum.ECDSA_ENCRYPT_ERROR.getRetMsg(), e); 107 | } 108 | } 109 | 110 | /** 111 | * generatePubkeyByPrvkey: 通过私钥计算相应公钥.
112 | * 113 | * @author Rony 114 | * @param privateKey 115 | * 私钥字符串 116 | * @return 返回公钥字符串 117 | * @throws TrustSDKException TrustSDKException 118 | * @since JDK 1.7 119 | */ 120 | public static String generatePubkeyByPrvkey(String privateKey) throws TrustSDKException { 121 | return generatePubkeyByPrvkey(privateKey, false); 122 | } 123 | 124 | public static String decodePubkey(String encodePubKey) throws TrustSDKException { 125 | if (StringUtils.isEmpty(encodePubKey)) { 126 | throw new TrustSDKException(ErrorNum.INVALID_PARAM_ERROR.getRetCode(), ErrorNum.INVALID_PARAM_ERROR.getRetMsg()); 127 | } 128 | try { 129 | return ECDSAAlgorithm.decodePublicKey(encodePubKey); 130 | } catch (Exception e) { 131 | throw new TrustSDKException(ErrorNum.ECDSA_ENCRYPT_ERROR.getRetCode(), ErrorNum.ECDSA_ENCRYPT_ERROR.getRetMsg(), e); 132 | } 133 | } 134 | 135 | /** 136 | * generateAddrByPubkey:通过公钥获取对应地址.
137 | * 138 | * @author Rony 139 | * @param pubKey 140 | * 公钥字符串 141 | * @return address 142 | * @throws TrustSDKException 143 | * TrustSDKException 144 | * @since JDK 1.7 145 | */ 146 | public static String generateAddrByPubkey(String pubKey) throws TrustSDKException { 147 | if (StringUtils.isEmpty(pubKey)) { 148 | throw new TrustSDKException(ErrorNum.INVALID_PARAM_ERROR.getRetCode(), ErrorNum.INVALID_PARAM_ERROR.getRetMsg()); 149 | } 150 | try { 151 | return ECDSAAlgorithm.getAddress(Base64.decodeBase64(pubKey)); 152 | } catch (Exception e) { 153 | throw new TrustSDKException(ErrorNum.ECDSA_ENCRYPT_ERROR.getRetCode(), ErrorNum.ECDSA_ENCRYPT_ERROR.getRetMsg(), e); 154 | } 155 | } 156 | 157 | /** 158 | * generateAddrByPrvkey:通过私钥计算相应地址.
159 | * 160 | * @author Rony 161 | * @param privateKey 162 | * 私钥字符串 163 | * @return Address 164 | * @throws TrustSDKException TrustSDKException 165 | * @since JDK 1.7 166 | */ 167 | public static String generateAddrByPrvkey(String privateKey) throws TrustSDKException { 168 | if (StringUtils.isEmpty(privateKey)) { 169 | throw new TrustSDKException(ErrorNum.INVALID_PARAM_ERROR.getRetCode(), ErrorNum.INVALID_PARAM_ERROR.getRetMsg()); 170 | } 171 | try { 172 | String pubKey = ECDSAAlgorithm.generatePublicKey(privateKey); 173 | return generateAddrByPubkey(pubKey); 174 | } catch (Exception e) { 175 | throw new TrustSDKException(ErrorNum.ECDSA_ENCRYPT_ERROR.getRetCode(), ErrorNum.ECDSA_ENCRYPT_ERROR.getRetMsg(), e); 176 | } 177 | } 178 | 179 | /** 180 | * signString:为字符串进行签名, 并返回签名.
181 | * 182 | * @author Rony 183 | * @param privateKey 184 | * 私钥字符串 185 | * @param data 186 | * 需要签名的字符数组 187 | * @return 返回签名字符串 188 | * @throws TrustSDKException TrustSDKException 189 | * @since JDK 1.7 190 | */ 191 | public static String signString(String privateKey, byte[] data) throws TrustSDKException { 192 | if (StringUtils.isEmpty(privateKey)) { 193 | throw new TrustSDKException(ErrorNum.INVALID_PARAM_ERROR.getRetCode(), ErrorNum.INVALID_PARAM_ERROR.getRetMsg()); 194 | } 195 | try { 196 | return ECDSAAlgorithm.sign(privateKey, data); 197 | } catch (Exception e) { 198 | throw new TrustSDKException(ErrorNum.SIGN_ERROR.getRetCode(), ErrorNum.SIGN_ERROR.getRetMsg(), e); 199 | } 200 | } 201 | 202 | public static String signString(String privateKey, String data) throws TrustSDKException, UnsupportedEncodingException { 203 | return signString(privateKey, data.getBytes("UTF-8")); 204 | } 205 | 206 | /** 207 | * verifyString:验证一个签名是否有效.
208 | * 209 | * @author Rony 210 | * @param pubKey 211 | * 公钥字符串 212 | * @param srcString 213 | * 源字符串 214 | * @param sign 215 | * 签名字符串 216 | * @return 返回验证是否通过 true:验证成功 false:验证失败 217 | * @throws TrustSDKException TrustSDKException 218 | * @since JDK 1.7 219 | */ 220 | public static boolean verifyString(String pubKey, String srcString, String sign) throws TrustSDKException { 221 | if (StringUtils.isEmpty(pubKey) || StringUtils.isEmpty(srcString) || StringUtils.isEmpty(sign)) { 222 | throw new TrustSDKException(ErrorNum.INVALID_PARAM_ERROR.getRetCode(), ErrorNum.INVALID_PARAM_ERROR.getRetMsg()); 223 | } 224 | try { 225 | return ECDSAAlgorithm.verify(srcString, sign, pubKey); 226 | } catch (Exception e) { 227 | throw new TrustSDKException(ErrorNum.ECDSA_ENCRYPT_ERROR.getRetCode(), ErrorNum.ECDSA_ENCRYPT_ERROR.getRetMsg(), e); 228 | } 229 | } 230 | 231 | 232 | } 233 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/common/encrypt/algorithm/AESAlgorithm.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Project Name:trustsql_sdk 3 | * File Name:EncryptUtil.java 4 | * Package Name:com.tencent.trustsql.sdk.util 5 | * Date:Jul 26, 20172:48:58 PM 6 | * Copyright (c) 2017, Tencent All Rights Reserved. 7 | * 8 | */ 9 | 10 | package xyz.huanxicloud.blockchainj.core.common.encrypt.algorithm; 11 | 12 | import javax.crypto.Cipher; 13 | import javax.crypto.spec.SecretKeySpec; 14 | 15 | /** 16 | * ClassName:EncryptUtil
17 | * Date: Jul 26, 2017 2:48:58 PM
18 | * 19 | * @author Rony 20 | * @since JDK 1.7 21 | */ 22 | public class AESAlgorithm { 23 | 24 | /** 25 | * aesEncode:aes 加密.
26 | * 27 | * @author Rony 28 | * @param key 29 | * 秘钥 30 | * @param data 31 | * 明文 32 | */ 33 | public static byte[] aesEncode(byte[] key, byte[] data) throws Exception { 34 | Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); 35 | SecretKeySpec secretKey = new SecretKeySpec(key, "AES"); 36 | cipher.init(Cipher.ENCRYPT_MODE, secretKey); 37 | return cipher.doFinal(data); 38 | } 39 | 40 | /** 41 | * aesDecode: aes 解密.
42 | * 43 | * @author Rony 44 | * @param key key 45 | * @param encryptedText encryptedText 46 | * @return encryptedText 47 | * @throws Exception Exception 48 | * @since JDK 1.7 49 | */ 50 | public static byte[] aesDecode(byte[] key, byte[] encryptedText) throws Exception { 51 | Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING"); 52 | SecretKeySpec secretKey = new SecretKeySpec(key, "AES"); 53 | cipher.init(Cipher.DECRYPT_MODE, secretKey); 54 | return cipher.doFinal(encryptedText); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/common/encrypt/algorithm/Base58Algorithm.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Project Name:trustsql_sdk 3 | * File Name:Base58.java 4 | * Package Name:com.tencent.trustsql.sdk.util 5 | * Date:Jul 26, 20172:48:58 PM 6 | * Copyright (c) 2017, Tencent All Rights Reserved. 7 | * 8 | */ 9 | package xyz.huanxicloud.blockchainj.core.common.encrypt.algorithm; 10 | 11 | import java.math.BigInteger; 12 | import java.util.Arrays; 13 | 14 | /** 15 | * Base58 is a way to encode Bitcoin addresses (or arbitrary data) as 16 | * alphanumeric strings. 17 | *

18 | * Note that this is not the same base58 as used by Flickr, which you may find 19 | * referenced around the Internet. 20 | *

21 | * You may want to consider working with {@link VersionedChecksummedBytes} 22 | * instead, which adds support for testing the prefix and suffix bytes commonly 23 | * found in addresses. 24 | *

25 | * Satoshi explains: why base-58 instead of standard base-64 encoding? 26 | *

    27 | *
  • Don't want 0OIl characters that look the same in some fonts and could be 28 | * used to create visually identical looking account numbers.
  • 29 | *
  • A string with non-alphanumeric characters is not as easily accepted as an 30 | * account number.
  • 31 | *
  • E-mail usually won't line-break if there's no punctuation to break at.
  • 32 | *
  • Doubleclicking selects the whole number as one word if it's all 33 | * alphanumeric.
  • 34 | *
35 | *

36 | * However, note that the encoding/decoding runs in O(n²) time, so it is 37 | * not useful for large data. 38 | *

39 | * The basic idea of the encoding is to treat the data bytes as a large number 40 | * represented using base-256 digits, convert the number to be represented using 41 | * base-58 digits, preserve the exact number of leading zeros (which are 42 | * otherwise lost during the mathematical operations on the numbers), and 43 | * finally represent the resulting base-58 digits as alphanumeric ASCII 44 | * characters. 45 | */ 46 | public class Base58Algorithm { 47 | public static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray(); 48 | private static final char ENCODED_ZERO = ALPHABET[0]; 49 | private static final int[] INDEXES = new int[128]; 50 | static { 51 | Arrays.fill(INDEXES, -1); 52 | for (int i = 0; i < ALPHABET.length; i++) { 53 | INDEXES[ALPHABET[i]] = i; 54 | } 55 | } 56 | 57 | /** 58 | * Encodes the given bytes as a base58 string (no checksum is appended). 59 | * 60 | * @param input 61 | * the bytes to encode 62 | * @return the base58-encoded string 63 | */ 64 | public static String encode(byte[] input) { 65 | if (input.length == 0) { 66 | return ""; 67 | } 68 | // Count leading zeros. 69 | int zeros = 0; 70 | while (zeros < input.length && input[zeros] == 0) { 71 | ++zeros; 72 | } 73 | // Convert base-256 digits to base-58 digits (plus conversion to ASCII 74 | // characters) 75 | input = Arrays.copyOf(input, input.length); // since we modify it 76 | // in-place 77 | char[] encoded = new char[input.length * 2]; // upper bound 78 | int outputStart = encoded.length; 79 | for (int inputStart = zeros; inputStart < input.length;) { 80 | encoded[--outputStart] = ALPHABET[divmod(input, inputStart, 256, 58)]; 81 | if (input[inputStart] == 0) { 82 | ++inputStart; // optimization - skip leading zeros 83 | } 84 | } 85 | // Preserve exactly as many leading encoded zeros in output as there 86 | // were leading zeros in input. 87 | while (outputStart < encoded.length && encoded[outputStart] == ENCODED_ZERO) { 88 | ++outputStart; 89 | } 90 | while (--zeros >= 0) { 91 | encoded[--outputStart] = ENCODED_ZERO; 92 | } 93 | // Return encoded string (including encoded leading zeros). 94 | return new String(encoded, outputStart, encoded.length - outputStart); 95 | } 96 | 97 | /** 98 | * Decodes the given base58 string into the original data bytes. 99 | * 100 | * @param input 101 | * the base58-encoded string to decode 102 | * @return the decoded data bytes 103 | * @throws AddressFormatException 104 | * if the given string is not a valid base58 string 105 | */ 106 | public static byte[] decode(String input) throws RuntimeException { 107 | if (input.length() == 0) { 108 | return new byte[0]; 109 | } 110 | // Convert the base58-encoded ASCII chars to a base58 byte sequence 111 | // (base58 digits). 112 | byte[] input58 = new byte[input.length()]; 113 | for (int i = 0; i < input.length(); ++i) { 114 | char c = input.charAt(i); 115 | int digit = c < 128 ? INDEXES[c] : -1; 116 | if (digit < 0) { 117 | throw new RuntimeException("Illegal character " + c + " at position " + i); 118 | } 119 | input58[i] = (byte) digit; 120 | } 121 | // Count leading zeros. 122 | int zeros = 0; 123 | while (zeros < input58.length && input58[zeros] == 0) { 124 | ++zeros; 125 | } 126 | // Convert base-58 digits to base-256 digits. 127 | byte[] decoded = new byte[input.length()]; 128 | int outputStart = decoded.length; 129 | for (int inputStart = zeros; inputStart < input58.length;) { 130 | decoded[--outputStart] = divmod(input58, inputStart, 58, 256); 131 | if (input58[inputStart] == 0) { 132 | ++inputStart; // optimization - skip leading zeros 133 | } 134 | } 135 | // Ignore extra leading zeroes that were added during the calculation. 136 | while (outputStart < decoded.length && decoded[outputStart] == 0) { 137 | ++outputStart; 138 | } 139 | // Return decoded data (including original number of leading zeros). 140 | return Arrays.copyOfRange(decoded, outputStart - zeros, decoded.length); 141 | } 142 | 143 | public static BigInteger decodeToBigInteger(String input) throws RuntimeException { 144 | return new BigInteger(1, decode(input)); 145 | } 146 | 147 | /** 148 | * Decodes the given base58 string into the original data bytes, using the 149 | * checksum in the last 4 bytes of the decoded data to verify that the rest 150 | * are correct. The checksum is removed from the returned data. 151 | * 152 | * @param input 153 | * the base58-encoded string to decode (which should include the 154 | * checksum) 155 | * @throws AddressFormatException 156 | * if the input is not base 58 or the checksum does not 157 | * validate. 158 | * 159 | * public static byte[] decodeChecked(String input) throws 160 | * AddressFormatException { byte[] decoded = decode(input); if 161 | * (decoded.length < 4) throw new 162 | * AddressFormatException("Input too short"); byte[] data = 163 | * Arrays.copyOfRange(decoded, 0, decoded.length - 4); byte[] 164 | * checksum = Arrays.copyOfRange(decoded, decoded.length - 4, 165 | * decoded.length); byte[] actualChecksum = 166 | * Arrays.copyOfRange(Sha256Hash.hashTwice(data), 0, 4); if 167 | * (!Arrays.equals(checksum, actualChecksum)) throw new 168 | * AddressFormatException("Checksum does not validate"); return 169 | * data; } 170 | */ 171 | 172 | /** 173 | * Divides a number, represented as an array of bytes each containing a 174 | * single digit in the specified base, by the given divisor. The given 175 | * number is modified in-place to contain the quotient, and the return value 176 | * is the remainder. 177 | * 178 | * @param number 179 | * the number to divide 180 | * @param firstDigit 181 | * the index within the array of the first non-zero digit (this 182 | * is used for optimization by skipping the leading zeros) 183 | * @param base 184 | * the base in which the number's digits are represented (up to 185 | * 256) 186 | * @param divisor 187 | * the number to divide by (up to 256) 188 | * @return the remainder of the division operation 189 | */ 190 | private static byte divmod(byte[] number, int firstDigit, int base, int divisor) { 191 | // this is just long division which accounts for the base of the input 192 | // digits 193 | int remainder = 0; 194 | for (int i = firstDigit; i < number.length; i++) { 195 | int digit = (int) number[i] & 0xFF; 196 | int temp = remainder * base + digit; 197 | number[i] = (byte) (temp / divisor); 198 | remainder = temp % divisor; 199 | } 200 | return (byte) remainder; 201 | } 202 | 203 | } 204 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/common/encrypt/algorithm/BaseAlgorithm.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Project Name:trustsql_sdk 3 | * File Name:BaseAlgoUtil.java 4 | * Package Name:com.tencent.trustsql.sdk.algo 5 | * Date:Jul 26, 20175:54:22 PM 6 | * Copyright (c) 2017, Tencent All Rights Reserved. 7 | * 8 | */ 9 | 10 | package xyz.huanxicloud.blockchainj.core.common.encrypt.algorithm; 11 | 12 | 13 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 14 | 15 | import java.security.MessageDigest; 16 | import java.security.Security; 17 | 18 | /** 19 | * ClassName:BaseAlgoUtil
20 | * Date: Jul 26, 2017 5:54:22 PM
21 | * 22 | * @author Rony 23 | * @since JDK 1.7 24 | */ 25 | public class BaseAlgorithm { 26 | 27 | static { 28 | Security.addProvider(new BouncyCastleProvider()); 29 | } 30 | 31 | /** 32 | * encode bytes 33 | * 34 | * @param algorithm algorithm 35 | * @param data data 36 | * @return byte[] 37 | */ 38 | public static byte[] encode(String algorithm, byte[] data) { 39 | if (data == null) { 40 | return null; 41 | } 42 | try { 43 | MessageDigest messageDigest = MessageDigest.getInstance(algorithm); 44 | messageDigest.update(data); 45 | return messageDigest.digest(); 46 | } catch (Exception e) { 47 | throw new RuntimeException(e); 48 | } 49 | } 50 | 51 | /** 52 | * encodeTwice bytes 53 | * 54 | * @param algorithm algorithm 55 | * @param data data 56 | * @return byte[] 57 | */ 58 | protected static byte[] encodeTwice(String algorithm, byte[] data) { 59 | if (data == null) { 60 | return null; 61 | } 62 | try { 63 | MessageDigest messageDigest = MessageDigest.getInstance(algorithm); 64 | messageDigest.update(data); 65 | return messageDigest.digest(messageDigest.digest()); 66 | } catch (Exception e) { 67 | throw new RuntimeException(e); 68 | } 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/common/encrypt/algorithm/DESAlgorithm.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Project Name:trustsql_sdk 3 | * File Name:DESAlgoUtil2.java 4 | * Package Name:com.tencent.trustsql.sdk.algo 5 | * Date:Jul 28, 201710:38:59 AM 6 | * Copyright (c) 2017, NUCC All Rights Reserved. 7 | * 8 | */ 9 | 10 | package xyz.huanxicloud.blockchainj.core.common.encrypt.algorithm; 11 | 12 | import javax.crypto.Cipher; 13 | import javax.crypto.SecretKeyFactory; 14 | import javax.crypto.spec.DESedeKeySpec; 15 | import java.security.Key; 16 | 17 | /** 18 | * ClassName:DESAlgoUtil2
19 | * Function: TODO ADD FUNCTION.
20 | * Reason: TODO ADD REASON.
21 | * Date: Jul 28, 2017 10:38:59 AM
22 | * @author Rony 23 | * @since JDK 1.7 24 | */ 25 | public class DESAlgorithm { 26 | /** 27 | * 密钥算法 28 | * */ 29 | public static final String KEY_ALGORITHM = "DESede"; 30 | 31 | /** 32 | * 加密/解密算法/工作模式/填充方式 33 | * */ 34 | public static final String CIPHER_ALGORITHM = "DESede/ECB/PKCS5Padding"; 35 | 36 | /** 37 | * 转换密钥 38 | * 39 | * @param key 40 | * 二进制密钥 41 | * @return Key 密钥 42 | * */ 43 | public static Key toKey(byte[] key) throws Exception { 44 | // 实例化Des密钥 45 | DESedeKeySpec dks = new DESedeKeySpec(key); 46 | // 实例化密钥工厂 47 | SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(KEY_ALGORITHM); 48 | // 生成密钥 49 | return keyFactory.generateSecret(dks); 50 | } 51 | 52 | /** 53 | * 加密数据 54 | * 55 | * @param data 56 | * 待加密数据 57 | * @param key 58 | * 密钥 59 | * @return byte[] 加密后的数据 60 | * */ 61 | public static byte[] encrypt(byte[] data, byte[] key) throws Exception { 62 | // 还原密钥 63 | Key k = toKey(key); 64 | // 实例化 65 | Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); 66 | // 初始化,设置为加密模式 67 | cipher.init(Cipher.ENCRYPT_MODE, k); 68 | // 执行操作 69 | return cipher.doFinal(data); 70 | } 71 | 72 | /** 73 | * 解密数据 74 | * 75 | * @param data 76 | * 待解密数据 77 | * @param key 78 | * 密钥 79 | * @return byte[] 解密后的数据 80 | * */ 81 | public static byte[] decrypt(byte[] data, byte[] key) throws Exception { 82 | // 欢迎密钥 83 | Key k = toKey(key); 84 | // 实例化 85 | Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); 86 | // 初始化,设置为解密模式 87 | cipher.init(Cipher.DECRYPT_MODE, k); 88 | // 执行操作 89 | return cipher.doFinal(data); 90 | } 91 | } 92 | 93 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/common/encrypt/algorithm/ECDSAAlgorithm.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Project Name:trustsql_sdk 3 | * File Name:ECDSAAlgoUtil.java 4 | * Package Name:com.tencent.trustsql.sdk.algo 5 | * Date:Jul 26, 20175:17:04 PM 6 | * Copyright (c) 2017, Tencent All Rights Reserved. 7 | */ 8 | 9 | package xyz.huanxicloud.blockchainj.core.common.encrypt.algorithm; 10 | 11 | 12 | import com.google.common.base.Objects; 13 | import org.apache.tomcat.util.codec.binary.Base64; 14 | import org.bouncycastle.jce.ECNamedCurveTable; 15 | import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec; 16 | import org.bouncycastle.math.ec.ECPoint; 17 | import org.spongycastle.asn1.ASN1InputStream; 18 | import org.spongycastle.asn1.ASN1Integer; 19 | import org.spongycastle.asn1.DERSequenceGenerator; 20 | import org.spongycastle.asn1.DLSequence; 21 | import org.spongycastle.asn1.x9.X9ECParameters; 22 | import org.spongycastle.crypto.digests.SHA256Digest; 23 | import org.spongycastle.crypto.ec.CustomNamedCurves; 24 | import org.spongycastle.crypto.params.ECDomainParameters; 25 | import org.spongycastle.crypto.params.ECPrivateKeyParameters; 26 | import org.spongycastle.crypto.params.ECPublicKeyParameters; 27 | import org.spongycastle.crypto.signers.ECDSASigner; 28 | import org.spongycastle.crypto.signers.HMacDSAKCalculator; 29 | import org.spongycastle.math.ec.FixedPointUtil; 30 | import xyz.huanxicloud.blockchainj.core.Constants; 31 | 32 | import java.io.ByteArrayOutputStream; 33 | import java.io.IOException; 34 | import java.io.UnsupportedEncodingException; 35 | import java.math.BigInteger; 36 | import java.security.MessageDigest; 37 | import java.security.SecureRandom; 38 | 39 | /** 40 | * ClassName:ECDSAAlgoUtil
41 | * Date: Jul 26, 2017 5:17:04 PM
42 | * 43 | * @author Rony 44 | * @since JDK 1.7 45 | */ 46 | public class ECDSAAlgorithm { 47 | public static final ECDomainParameters CURVE; 48 | public static final BigInteger HALF_CURVE_ORDER; 49 | 50 | static { 51 | X9ECParameters CURVE_PARAMS = CustomNamedCurves.getByName("secp256k1"); 52 | FixedPointUtil.precompute(CURVE_PARAMS.getG(), 12); 53 | CURVE = new ECDomainParameters(CURVE_PARAMS.getCurve(), CURVE_PARAMS.getG(), CURVE_PARAMS.getN(), 54 | CURVE_PARAMS.getH()); 55 | HALF_CURVE_ORDER = CURVE_PARAMS.getN().shiftRight(1); 56 | } 57 | 58 | public static String generatePrivateKey() { 59 | SecureRandom secureRandom; 60 | try { 61 | secureRandom = SecureRandom.getInstance(Constants.RANDOM_NUMBER_ALGORITHM, 62 | Constants.RANDOM_NUMBER_ALGORITHM_PROVIDER); 63 | } catch (Exception e) { 64 | secureRandom = new SecureRandom(); 65 | } 66 | // Generate the key, skipping as many as desired. 67 | byte[] privateKeyAttempt = new byte[32]; 68 | secureRandom.nextBytes(privateKeyAttempt); 69 | BigInteger privateKeyCheck = new BigInteger(1, privateKeyAttempt); 70 | while (privateKeyCheck.compareTo(BigInteger.ZERO) == 0 || privateKeyCheck.compareTo(Constants.MAXPRIVATEKEY) > 0) { 71 | secureRandom.nextBytes(privateKeyAttempt); 72 | privateKeyCheck = new BigInteger(1, privateKeyAttempt); 73 | } 74 | String result = Base64.encodeBase64String(privateKeyAttempt); 75 | result = result.replaceAll("[\\s*\t\n\r]", ""); 76 | return result; 77 | } 78 | 79 | /** 80 | * 生成公钥,encode为true时为短公钥 81 | * 82 | * @param privateKeyBase64String 私钥 83 | * @param encode 是否使用base64缩短 84 | * @return 公钥 85 | */ 86 | public static String generatePublicKey(String privateKeyBase64String, boolean encode) { 87 | try { 88 | byte[] privateKeyBytes = Base64.decodeBase64(privateKeyBase64String); 89 | ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1"); 90 | ECPoint pointQ = spec.getG().multiply(new BigInteger(1, privateKeyBytes)); 91 | String result = Base64.encodeBase64String(pointQ.getEncoded(encode)); 92 | result = result.replaceAll("[\\s*\t\n\r]", ""); 93 | return result; 94 | } catch (Exception e) { 95 | throw new RuntimeException(e); 96 | } 97 | } 98 | 99 | /** 100 | * 生成长公钥 101 | * 102 | * @param privateKeyBase64String 私钥 103 | * @return 公钥 104 | */ 105 | public static String generatePublicKey(String privateKeyBase64String) { 106 | return generatePublicKey(privateKeyBase64String, false); 107 | } 108 | 109 | public static String decodePublicKey(String encodePubKeyBase64String) { 110 | try { 111 | byte[] encodePubkeyBytes = Base64.decodeBase64(encodePubKeyBase64String); 112 | ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1"); 113 | ECPoint pointQ = spec.getG().getCurve().decodePoint(encodePubkeyBytes); 114 | String result = Base64.encodeBase64String(pointQ.getEncoded(false)); 115 | result = result.replaceAll("[\\s*\t\n\r]", ""); 116 | return result; 117 | } catch (Exception e) { 118 | throw new RuntimeException(e); 119 | } 120 | } 121 | 122 | /** 123 | * 测试使用私钥签名,并使用公钥验证签名 124 | */ 125 | public static void main(String[] args) throws Exception { 126 | String priKey = generatePrivateKey(); 127 | System.out.println(priKey); 128 | String pubKey = generatePublicKey(priKey, true); 129 | String pubKey1 = generatePublicKey(priKey); 130 | System.out.println(pubKey); 131 | System.out.println(pubKey1); 132 | String sign = sign(priKey, "abc"); 133 | System.out.println(sign); 134 | boolean verify = verify("abc", sign, pubKey); 135 | System.out.println(verify); 136 | } 137 | 138 | /** 139 | * 根据公钥生成address 140 | * 141 | * @param publicKey 公钥 142 | * @return Address 143 | * @throws Exception exception 144 | */ 145 | public static String getAddress(String publicKey) throws Exception { 146 | return getAddress(publicKey.getBytes("UTF-8"), 0); 147 | } 148 | 149 | /** 150 | * 根据公钥生成地址 151 | * 152 | * @param keyBytes 公钥 153 | * @param version 版本,可以不用 154 | * @return address 155 | * @throws Exception exception 156 | */ 157 | public static String getAddress(byte[] keyBytes, int... version) throws Exception { 158 | byte[] hashSha256 = BaseAlgorithm.encode("SHA-256", keyBytes); 159 | MessageDigest messageDigest = MessageDigest.getInstance("RipeMD160"); 160 | messageDigest.update(hashSha256); 161 | // byte[] hashRipeMD160 = messageDigest.digest(); 162 | // byte[] versionHashBytes = new byte[1 + hashRipeMD160.length]; 163 | // if(version == null || version.length == 0) { 164 | // versionHashBytes[0] = 0; 165 | // } else { 166 | // versionHashBytes[0] = (byte) version[0]; 167 | // } 168 | // System.arraycopy(hashRipeMD160, 0, versionHashBytes, 1, hashRipeMD160.length); 169 | // byte[] checkSumBytes = BaseAlgorithm.encodeTwice("SHA-256", versionHashBytes); 170 | // byte[] rawAddr = new byte[versionHashBytes.length + 4]; 171 | // System.arraycopy(versionHashBytes, 0, rawAddr, 0, versionHashBytes.length); 172 | // System.arraycopy(checkSumBytes, 0, rawAddr, versionHashBytes.length, 4); 173 | byte[] hashRipeMD160 = messageDigest.digest(); 174 | byte[] hashDoubleSha256 = BaseAlgorithm.encodeTwice("SHA-256", hashRipeMD160); 175 | byte[] rawAddr = new byte[1 + hashRipeMD160.length + 4]; 176 | rawAddr[0] = 0; 177 | System.arraycopy(hashRipeMD160, 0, rawAddr, 1, hashRipeMD160.length); 178 | System.arraycopy(hashDoubleSha256, 0, rawAddr, hashRipeMD160.length + 1, 4); 179 | return Base58Algorithm.encode(rawAddr); 180 | } 181 | 182 | public static String sign(String privateKey, String data) throws UnsupportedEncodingException { 183 | return sign(privateKey, data.getBytes("UTF-8")); 184 | } 185 | 186 | public static String sign(String privateKey, byte[] data) { 187 | byte[] hash256 = BaseAlgorithm.encode("SHA-256", data); 188 | ECDSASigner signer = new ECDSASigner(new HMacDSAKCalculator(new SHA256Digest())); 189 | BigInteger pri = new BigInteger(1, Base64.decodeBase64(privateKey)); 190 | ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(pri, CURVE); 191 | signer.init(true, privKey); 192 | BigInteger[] components = signer.generateSignature(hash256); 193 | byte[] content = new ECDSASignature(components[0], components[1]).toCanonicalised().encodeToDER(); 194 | String result = Base64.encodeBase64String(content); 195 | result = result.replaceAll("[\\s*\t\n\r]", ""); 196 | return result; 197 | } 198 | 199 | /** 200 | * 根据公钥验证签名是否合法 201 | * 202 | * @param srcStr 明文字符串 203 | * @param sign 用私钥签过名的sign 204 | * @param pubKey 公钥 205 | * @return 是否校验通过 206 | * @throws Exception Exception 207 | */ 208 | public static boolean verify(String srcStr, String sign, String pubKey) throws Exception { 209 | byte[] hash256 = BaseAlgorithm.encode("SHA-256", srcStr.getBytes("UTF-8")); 210 | ECDSASignature eCDSASignature = ECDSASignature.decodeFromDER(Base64.decodeBase64(sign)); 211 | ECDSASigner signer = new ECDSASigner(); 212 | org.spongycastle.math.ec.ECPoint pub = CURVE.getCurve().decodePoint(Base64.decodeBase64(pubKey)); 213 | @SuppressWarnings("all") 214 | ECPublicKeyParameters params = new ECPublicKeyParameters(CURVE.getCurve().decodePoint(pub.getEncoded()), CURVE); 215 | signer.init(false, params); 216 | return signer.verifySignature(hash256, eCDSASignature.r, eCDSASignature.s); 217 | } 218 | 219 | public static class ECDSASignature { 220 | /** 221 | * The two components of the signature. 222 | */ 223 | public final BigInteger r, s; 224 | 225 | /** 226 | * Constructs a signature with the given components. Does NOT 227 | * automatically canonicalise the signature. 228 | */ 229 | public ECDSASignature(BigInteger r, BigInteger s) { 230 | this.r = r; 231 | this.s = s; 232 | } 233 | 234 | /** 235 | * Returns true if the S component is "low", that means it is below 236 | * See 238 | * BIP62. 239 | */ 240 | public boolean isCanonical() { 241 | return s.compareTo(HALF_CURVE_ORDER) <= 0; 242 | } 243 | 244 | /** 245 | * Will automatically adjust the S component to be less than or equal to 246 | * half the curve order, if necessary. This is required because for 247 | * every signature (r,s) the signature (r, -s (mod N)) is a valid 248 | * signature of the same message. However, we dislike the ability to 249 | * modify the bits of a Bitcoin transaction after it's been signed, as 250 | * that violates various assumed invariants. Thus in future only one of 251 | * those forms will be considered legal and the other will be banned. 252 | */ 253 | public ECDSASignature toCanonicalised() { 254 | if (!isCanonical()) { 255 | // The order of the curve is the number of valid points that 256 | // exist on that curve. If S is in the upper 257 | // half of the number of valid points, then bring it back to the 258 | // lower half. Otherwise, imagine that 259 | // N = 10 260 | // s = 8, so (-8 % 10 == 2) thus both (r, 8) and (r, 2) are 261 | // valid solutions. 262 | // 10 - 8 == 2, giving us always the latter solution, which is 263 | // canonical. 264 | return new ECDSASignature(r, CURVE.getN().subtract(s)); 265 | } else { 266 | return this; 267 | } 268 | } 269 | 270 | /** 271 | * DER is an international standard for serializing data structures 272 | * which is widely used in cryptography. It's somewhat like protocol 273 | * buffers but less convenient. This method returns a standard DER 274 | * encoding of the signature, as recognized by OpenSSL and other 275 | * libraries. 276 | */ 277 | public byte[] encodeToDER() { 278 | try { 279 | return derByteStream().toByteArray(); 280 | } catch (IOException e) { 281 | // Cannot happen. 282 | throw new RuntimeException(e); 283 | } 284 | } 285 | 286 | public static ECDSASignature decodeFromDER(byte[] bytes) { 287 | ASN1InputStream decoder = null; 288 | try { 289 | decoder = new ASN1InputStream(bytes); 290 | DLSequence seq = (DLSequence) decoder.readObject(); 291 | if (seq == null) { 292 | throw new RuntimeException("Reached past end of ASN.1 stream."); 293 | } 294 | ASN1Integer r, s; 295 | try { 296 | r = (ASN1Integer) seq.getObjectAt(0); 297 | s = (ASN1Integer) seq.getObjectAt(1); 298 | } catch (ClassCastException e) { 299 | throw new IllegalArgumentException(e); 300 | } 301 | // OpenSSL deviates from the DER spec by interpreting these 302 | // values as unsigned, though they should not be 303 | // Thus, we always use the positive versions. See: 304 | // http://r6.ca/blog/20111119T211504Z.html 305 | return new ECDSASignature(r.getPositiveValue(), s.getPositiveValue()); 306 | } catch (IOException e) { 307 | throw new RuntimeException(e); 308 | } finally { 309 | if (decoder != null) { 310 | try { 311 | decoder.close(); 312 | } catch (IOException x) { 313 | } 314 | } 315 | } 316 | } 317 | 318 | protected ByteArrayOutputStream derByteStream() throws IOException { 319 | // Usually 70-72 bytes. 320 | ByteArrayOutputStream bos = new ByteArrayOutputStream(72); 321 | DERSequenceGenerator seq = new DERSequenceGenerator(bos); 322 | seq.addObject(new ASN1Integer(r)); 323 | seq.addObject(new ASN1Integer(s)); 324 | seq.close(); 325 | return bos; 326 | } 327 | 328 | @Override 329 | public boolean equals(Object o) { 330 | if (this == o) { 331 | return true; 332 | } 333 | if (o == null || getClass() != o.getClass()) { 334 | return false; 335 | } 336 | ECDSASignature other = (ECDSASignature) o; 337 | return r.equals(other.r) && s.equals(other.s); 338 | } 339 | 340 | @Override 341 | public int hashCode() { 342 | return Objects.hashCode(r, s); 343 | } 344 | } 345 | 346 | } 347 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/common/encrypt/exception/ErrorNum.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Project Name:trustsql_sdk 3 | * File Name:ErrorNum.java 4 | * Package Name:com.tencent.trustsql.sdk 5 | * Date:Jul 26, 20172:59:02 PM 6 | * Copyright (c) 2017, Tencent All Rights Reserved. 7 | * 8 | */ 9 | 10 | package xyz.huanxicloud.blockchainj.core.common.encrypt.exception; 11 | /** 12 | * ClassName:ErrorNum
13 | * Date: Jul 26, 2017 2:59:02 PM
14 | * @author Rony 15 | * @version 16 | * @since JDK 1.7 17 | * @see 18 | */ 19 | public enum ErrorNum { 20 | 21 | INVALID_PARAM_ERROR("001", "参数错误"), 22 | DES3_ENCRYPT_ERROR("002", "DES3加解密错误"), 23 | AES_ENCRYPT_ERROR("003", "AES加解密错误"), 24 | ECDSA_ENCRYPT_ERROR("004", "ECDSA加解密错误"), 25 | SIGN_ERROR("005", "签名错误"), 26 | GENERATE_SIGN_ERROR("006", "生成签名错误"), 27 | GENERATE_SQL_ERROR("007", "生成SQL错误"), 28 | VERIFY_SIGN_ERROR("008", "验证签名错误"); 29 | 30 | private String retCode; 31 | private String retMsg; 32 | 33 | ErrorNum(String retCode, String retMsg) { 34 | this.retCode = retCode; 35 | this.retMsg = retMsg; 36 | } 37 | 38 | public String getRetCode() { 39 | return retCode; 40 | } 41 | 42 | public void setRetCode(String retCode) { 43 | this.retCode = retCode; 44 | } 45 | 46 | public String getRetMsg() { 47 | return retMsg; 48 | } 49 | 50 | public void setRetMsg(String retMsg) { 51 | this.retMsg = retMsg; 52 | } 53 | } 54 | 55 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/common/encrypt/exception/TrustSDKException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Project Name:trustsql_sdk 3 | * File Name:TrustSDKException.java 4 | * Package Name:com.tencent.trustsql.sdk.exception 5 | * Date:Jul 26, 201711:24:06 AM 6 | * Copyright (c) 2017, Tencent All Rights Reserved. 7 | * 8 | */ 9 | 10 | package xyz.huanxicloud.blockchainj.core.common.encrypt.exception; 11 | 12 | import com.alibaba.fastjson.JSONObject; 13 | 14 | /** 15 | * ClassName:TrustSDKException
16 | * Date: Jul 26, 2017 11:24:06 AM
17 | * @author Rony 18 | * @version 19 | * @since JDK 1.7 20 | * @see 21 | */ 22 | public class TrustSDKException extends Exception { 23 | 24 | private static final long serialVersionUID = -4214831807802264420L; 25 | 26 | protected String rtnCd; 27 | protected String rtnMsg; 28 | 29 | public TrustSDKException(String rtnCd, String rtnMsg) { 30 | super(rtnMsg); 31 | this.rtnCd = rtnCd; 32 | this.rtnMsg = rtnMsg; 33 | } 34 | 35 | public TrustSDKException(String rtnCd, String rtnMsg, Throwable t) { 36 | super(rtnMsg, t); 37 | this.rtnCd = rtnCd; 38 | this.rtnMsg = rtnMsg; 39 | } 40 | 41 | public String getRtnCd() { 42 | return rtnCd; 43 | } 44 | 45 | public void setRtnCd(String rtnCd) { 46 | this.rtnCd = rtnCd; 47 | } 48 | 49 | public String getRtnMsg() { 50 | return rtnMsg; 51 | } 52 | 53 | public void setRtnMsg(String rtnMsg) { 54 | this.rtnMsg = rtnMsg; 55 | } 56 | 57 | @Override 58 | public String toString() { 59 | return JSONObject.toJSONString(this); 60 | } 61 | } 62 | 63 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/common/returnmsg/ResultCode.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.core.common.returnmsg; 2 | 3 | /** 4 | * @author wuweifeng wrote on 2017/10/23. 5 | */ 6 | public enum ResultCode { 7 | //成功 8 | SUCCESS(200), 9 | //失败 10 | FAIL(400), 11 | //未认证(签名错误) 12 | UNAUTHORIZED(401), 13 | //没有登录 14 | NO_LOGIN(402), 15 | //没有权限 16 | NO_PERMISSION(403), 17 | //接口不存在 18 | NOT_FOUND(404), 19 | //用户状态异常、公司状态异常、产品状态异常 20 | STATE_ERROR(406), 21 | //服务器内部错误 22 | INTERNAL_SERVER_ERROR(500), 23 | //参数错误 24 | PARAMETER_ERROR(10001), 25 | //账号错误 26 | ACCOUNT_ERROR(20001), 27 | //登录失败 28 | LOGIN_FAIL_ERROR(20002); 29 | 30 | 31 | public int code; 32 | 33 | ResultCode(int code) { 34 | this.code = code; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/common/returnmsg/ReturnMessage.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.core.common.returnmsg; 2 | 3 | /** 4 | * @author wuweifeng wrote on 2017/10/23. 5 | */ 6 | public class ReturnMessage { 7 | private int code; 8 | private Object msg; 9 | 10 | @Override 11 | public String toString() { 12 | return "ReturnMessage{" + 13 | "code=" + code + 14 | ", data=" + msg + 15 | '}'; 16 | } 17 | 18 | public ReturnMessage(int code, Object msg) { 19 | this.code = code; 20 | this.msg = msg; 21 | } 22 | 23 | public ReturnMessage setCode(ResultCode resultCode) { 24 | this.code = resultCode.code; 25 | return this; 26 | } 27 | 28 | public int getCode() { 29 | return code; 30 | } 31 | 32 | public ReturnMessage setCode(int code) { 33 | this.code = code; 34 | return this; 35 | } 36 | 37 | public Object getMsg() { 38 | return msg; 39 | } 40 | 41 | public void setMsg(Object msg) { 42 | this.msg = msg; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/common/start/AppStart.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.core.common.start; 2 | 3 | import org.springframework.stereotype.Component; 4 | import xyz.huanxicloud.blockchainj.core.blockchain.BlockChain; 5 | import xyz.huanxicloud.blockchainj.core.network.client.SocketClientManager; 6 | 7 | import javax.annotation.PostConstruct; 8 | import javax.annotation.Resource; 9 | 10 | /** 11 | * @author: huanxi 12 | * @date: 2019/1/4 18:09 13 | */ 14 | @Component 15 | public class AppStart { 16 | @Resource 17 | BlockChain blockChain; 18 | @Resource 19 | SocketClientManager socketClientManager; 20 | @PostConstruct 21 | public void initApp() { 22 | //获取所有节点 23 | //连接各节点 24 | socketClientManager.start(); 25 | initDB(); 26 | } 27 | 28 | public void initDB() { 29 | //创建本地数据库 30 | 31 | //初始化表 32 | } 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/common/util/CommonUtils.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.core.common.util; 2 | 3 | /** 4 | * @author: huanxi 5 | * @date: 2019/1/1 20:40 6 | */ 7 | public class CommonUtils { 8 | public static Long getTimestamp() { 9 | return System.currentTimeMillis(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/common/util/FastJsonUtil.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.core.common.util; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.JSONObject; 5 | import com.alibaba.fastjson.serializer.JSONLibDataFormatSerializer; 6 | import com.alibaba.fastjson.serializer.SerializeConfig; 7 | import com.alibaba.fastjson.serializer.SerializerFeature; 8 | 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | /** 13 | * @author wuweifeng wrote on 2018/3/2. 14 | */ 15 | public class FastJsonUtil { 16 | private static final SerializeConfig CONFIG; 17 | 18 | static { 19 | CONFIG = new SerializeConfig(); 20 | CONFIG.put(java.util.Date.class, new JSONLibDataFormatSerializer()); // 使用和json-lib兼容的日期输出格式 21 | CONFIG.put(java.sql.Date.class, new JSONLibDataFormatSerializer()); // 使用和json-lib兼容的日期输出格式 22 | } 23 | 24 | private static final SerializerFeature[] FEATURES = {SerializerFeature.WriteMapNullValue, // 输出空置字段 25 | SerializerFeature.WriteNullListAsEmpty, // list字段如果为null,输出为[],而不是null 26 | SerializerFeature.WriteNullNumberAsZero, // 数值字段如果为null,输出为0,而不是null 27 | SerializerFeature.WriteNullBooleanAsFalse, // Boolean字段如果为null,输出为false,而不是null 28 | SerializerFeature.WriteNullStringAsEmpty // 字符类型字段如果为null,输出为"",而不是null 29 | }; 30 | 31 | 32 | public static String toJSONString(Object object) { 33 | return JSON.toJSONString(object, CONFIG, FEATURES); 34 | } 35 | 36 | public static String toJSONNoFeatures(Object object) { 37 | return JSON.toJSONString(object, CONFIG); 38 | } 39 | 40 | 41 | public static Object toBean(String text) { 42 | return JSON.parse(text); 43 | } 44 | 45 | public static T toBean(String text, Class clazz) { 46 | return JSON.parseObject(text, clazz); 47 | } 48 | 49 | /** 50 | * 转换为数组 51 | */ 52 | public static Object[] toArray(String text) { 53 | return toArray(text, null); 54 | } 55 | 56 | /** 57 | * 转换为数组 58 | */ 59 | public static Object[] toArray(String text, Class clazz) { 60 | return JSON.parseArray(text, clazz).toArray(); 61 | } 62 | 63 | /** 64 | * 转换为List 65 | */ 66 | public static List toList(String text, Class clazz) { 67 | return JSON.parseArray(text, clazz); 68 | } 69 | 70 | /** 71 | * 将string转化为序列化的json字符串 72 | */ 73 | public static Object textToJson(String text) { 74 | return JSON.parse(text); 75 | } 76 | 77 | /** 78 | * json字符串转化为map 79 | */ 80 | public static Map stringToCollect(String s) { 81 | return JSONObject.parseObject(s); 82 | } 83 | 84 | /** 85 | * 将map转化为string 86 | */ 87 | public static String collectToString(Map m) { 88 | return JSONObject.toJSONString(m); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/event/AddBlockEvent.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.core.event; 2 | 3 | import org.springframework.context.ApplicationEvent; 4 | import xyz.huanxicloud.blockchainj.core.blockchain.block.Block; 5 | 6 | /** 7 | * 确定生成block的Event(添加到rocksDB,执行sqlite语句,发布给其他节点) 8 | */ 9 | public class AddBlockEvent extends ApplicationEvent { 10 | public AddBlockEvent(Block block) { 11 | super(block); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/event/listener/BlockGeneratedListener.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.core.event.listener; 2 | 3 | import cn.hutool.log.Log; 4 | import cn.hutool.log.LogFactory; 5 | import org.springframework.context.event.EventListener; 6 | import org.springframework.stereotype.Component; 7 | import xyz.huanxicloud.blockchainj.core.blockchain.block.Block; 8 | import xyz.huanxicloud.blockchainj.core.event.AddBlockEvent; 9 | import xyz.huanxicloud.blockchainj.core.network.server.SocketServerManager; 10 | 11 | import javax.annotation.Resource; 12 | 13 | /** 14 | * @author: huanxi 15 | * @date: 2019/1/22 23:04 16 | * 生成区块时间监听(观察者模式) 17 | * 广播区块 18 | */ 19 | @Component 20 | public class BlockGeneratedListener { 21 | /** 22 | * 接收到添加区块事件 23 | * 24 | * @param addBlockEvent 25 | */ 26 | @Resource 27 | SocketServerManager server; 28 | private static Log log = LogFactory.get(SocketServerManager.class); 29 | 30 | @EventListener(AddBlockEvent.class) 31 | 32 | public void blockGenerated(AddBlockEvent addBlockEvent) { 33 | log.info("创建区块开始广播"); 34 | Block block = (Block) addBlockEvent.getSource(); 35 | server.broadcastBlock(block); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/network/client/Instruction.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.core.network.client; 2 | 3 | 4 | public class Instruction { 5 | public interface InstructionType { 6 | public static final int ALL = 0; 7 | public static final int ADD = 1; 8 | public static final int REMOVE = 2; 9 | } 10 | 11 | public Instruction() { 12 | } 13 | 14 | public Instruction(int type, Object msg) { 15 | this.type = type; 16 | this.msg = msg; 17 | } 18 | 19 | int type; 20 | Object msg; 21 | 22 | public int getType() { 23 | return type; 24 | } 25 | 26 | public void setType(int type) { 27 | this.type = type; 28 | } 29 | 30 | public Object getMsg() { 31 | return msg; 32 | } 33 | 34 | public void setMsg(Object msg) { 35 | this.msg = msg; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/network/client/NodeClient.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.core.network.client; 2 | 3 | import cn.hutool.log.Log; 4 | import cn.hutool.log.LogFactory; 5 | import com.alibaba.fastjson.JSON; 6 | import okhttp3.Request; 7 | import okhttp3.Response; 8 | import okhttp3.WebSocket; 9 | import okhttp3.WebSocketListener; 10 | 11 | import javax.annotation.Nullable; 12 | import java.util.List; 13 | 14 | /** 15 | * 节点客户端,监听节点变化 16 | * 连接节点种子服务器(带上自己地址,服务器广播有新的节点加入),获取所有节点,并且与其相连 17 | * 接受服务器节点发生变化指令,新增或删除对应节点 18 | * 19 | * @author: huanxi 20 | * @date: 2019/1/23 11:42 21 | */ 22 | public class NodeClient { 23 | private NodeClient() { 24 | } 25 | 26 | private static Log log = LogFactory.get(NodeClient.class); 27 | private static NodeClient nodeClient; 28 | 29 | public void connectWithAllNode(List ips) { 30 | for (String url : ips) { 31 | System.out.println("ws://" + url); 32 | // SocketClientManager.connectNode("ws://" + url); 33 | } 34 | } 35 | 36 | /** 37 | * 连接节点种子服务器 38 | * 39 | * @param nodeServer 种子服务器地址 40 | * @param myIp 我的地址 41 | */ 42 | public void connectNodeServer(String nodeServer, String myIp) { 43 | Request request = new Request.Builder() 44 | .url(nodeServer + myIp) 45 | .build(); 46 | SocketClientManager.getClient().newWebSocket(request, new WebSocketListener() { 47 | @Override 48 | public void onOpen(WebSocket webSocket, Response response) { 49 | super.onOpen(webSocket, response); 50 | } 51 | 52 | @Override 53 | public void onMessage(WebSocket webSocket, String text) { 54 | Instruction instruction = JSON.parseObject(text, Instruction.class); 55 | switch (instruction.type) { 56 | case Instruction.InstructionType.ALL: 57 | List ips = (List) instruction.msg; 58 | connectWithAllNode(ips); 59 | break; 60 | case Instruction.InstructionType.ADD: 61 | break; 62 | case Instruction.InstructionType.REMOVE: 63 | break; 64 | } 65 | } 66 | 67 | @Override 68 | public void onClosed(WebSocket webSocket, int code, String reason) { 69 | super.onClosed(webSocket, code, reason); 70 | System.out.println("连接关闭"); 71 | } 72 | 73 | @Override 74 | public void onFailure(WebSocket webSocket, Throwable t, @Nullable Response response) { 75 | log.error("节点服务器连接异常"); 76 | t.printStackTrace(); 77 | } 78 | }); 79 | } 80 | 81 | public static NodeClient getNodeClient() { 82 | if (nodeClient == null) nodeClient = new NodeClient(); 83 | return nodeClient; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/network/client/SocketClientManager.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.core.network.client; 2 | 3 | 4 | import cn.hutool.log.Log; 5 | import cn.hutool.log.LogFactory; 6 | import okhttp3.*; 7 | import org.springframework.stereotype.Component; 8 | import xyz.huanxicloud.blockchainj.config.AppProperty; 9 | 10 | import javax.annotation.Resource; 11 | import java.util.concurrent.ConcurrentHashMap; 12 | 13 | /** 14 | * @author: huanxi 15 | * @date: 2019/1/21 21:37 16 | */ 17 | @Component 18 | public class SocketClientManager { 19 | private static Log log = LogFactory.get(SocketClientManager.class); 20 | /** 21 | * 客户端连接set 22 | * 处理个节点广播消息 23 | */ 24 | private static ConcurrentHashMap clients = new ConcurrentHashMap<>(); 25 | private static OkHttpClient client = new OkHttpClient.Builder().build(); 26 | @Resource 27 | private AppProperty appProperty; 28 | 29 | public static void connectNode(String host) { 30 | log.info("开始连接节点" + host); 31 | String url = "ws://" + host + "/connect"; 32 | //连接 33 | Request request = new Request.Builder() 34 | .url(url) 35 | .build(); 36 | client.newWebSocket(request, new WebSocketListener() { 37 | @Override 38 | public void onOpen(WebSocket webSocket, Response response) { 39 | super.onOpen(webSocket, response); 40 | clients.put(webSocket, host); 41 | log.info("连接节点成功,当前节点总数:" + clients.size()); 42 | } 43 | 44 | @Override 45 | public void onClosed(WebSocket webSocket, int code, String reason) { 46 | super.onClosed(webSocket, code, reason); 47 | clients.remove(webSocket); 48 | log.info("有节点断开,当前节点总数:" + clients.size()); 49 | } 50 | 51 | @Override 52 | public void onMessage(WebSocket webSocket, String text) { 53 | super.onMessage(webSocket, text); 54 | log.info("收到消息:" + text); 55 | //收到区块消息 56 | //进入投票 57 | //收到投票消息 58 | //达到共识,执行智能合约 59 | } 60 | }); 61 | } 62 | 63 | public void start() { 64 | NodeClient.getNodeClient().connectNodeServer(appProperty.getNodeServer(), appProperty.getMyIp()); 65 | } 66 | 67 | public static ConcurrentHashMap getClients() { 68 | return clients; 69 | } 70 | 71 | public static OkHttpClient getClient() { 72 | return client; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/network/server/SocketServerManager.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.core.network.server; 2 | 3 | import cn.hutool.log.Log; 4 | import cn.hutool.log.LogFactory; 5 | import org.springframework.stereotype.Component; 6 | import xyz.huanxicloud.blockchainj.core.blockchain.block.Block; 7 | 8 | import javax.websocket.OnClose; 9 | import javax.websocket.OnError; 10 | import javax.websocket.OnOpen; 11 | import javax.websocket.Session; 12 | import javax.websocket.server.ServerEndpoint; 13 | import java.io.IOException; 14 | import java.util.concurrent.CopyOnWriteArraySet; 15 | 16 | /** 17 | * @author: huanxi 18 | * @date: 2019/1/21 20:55 19 | * 服务端启动 20 | */ 21 | @ServerEndpoint(value = "/connect") 22 | @Component 23 | public class SocketServerManager { 24 | 25 | private static CopyOnWriteArraySet sessions = new CopyOnWriteArraySet(); 26 | private static Log log = LogFactory.get(SocketServerManager.class); 27 | 28 | @OnOpen 29 | public void onOpen(Session session) { 30 | sessions.add(session); 31 | log.info("有节点连接,当前连接节点总数:" + sessions.size()); 32 | } 33 | 34 | @OnClose 35 | public void onClose(Session session) { 36 | sessions.remove(session); 37 | log.info("有节点断开,当前连接节点数:" + sessions.size()); 38 | } 39 | 40 | @OnError 41 | public void error(Session session, Throwable t) { 42 | log.info("有节点发生错误"); 43 | } 44 | 45 | public void broadcastBlock(Block block) { 46 | for (Session session : sessions) { 47 | try { 48 | session.getBasicRemote().sendText(block.toString()); 49 | } catch (IOException e) { 50 | log.info("广播失败"); 51 | e.printStackTrace(); 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/core/network/server/config/WebSocketConfig.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.core.network.server.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.socket.server.standard.ServerEndpointExporter; 6 | 7 | /** 8 | * @author: huanxi 9 | * @date: 2019/1/21 20:53 10 | */ 11 | @Configuration 12 | public class WebSocketConfig { 13 | @Bean 14 | public ServerEndpointExporter serverEndpointExporter() { 15 | return new ServerEndpointExporter(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/data/dao/RoleMapper.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.data.dao; 2 | 3 | import org.apache.ibatis.annotations.Mapper; 4 | import org.apache.ibatis.annotations.UpdateProvider; 5 | import org.springframework.stereotype.Component; 6 | 7 | /** 8 | * @author: huanxi 9 | * @date: 2019/1/21 20:12 10 | */ 11 | @Mapper 12 | @Component 13 | public interface RoleMapper { 14 | @UpdateProvider(type= RoleMapping.class,method = "test") 15 | void test(); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/data/dao/RoleMapping.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.data.dao; 2 | 3 | 4 | /** 5 | * @author: huanxi 6 | * @date: 2019/1/21 18:44 7 | */ 8 | public class RoleMapping { 9 | public String test(){ 10 | return "create table role(id int)"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/xyz/huanxicloud/blockchainj/data/pojo/Role.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj.data.pojo; 2 | 3 | /** 4 | * @author: huanxi 5 | * @date: 2019/1/21 18:42 6 | */ 7 | public class Role { 8 | } 9 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | #sqlite: 2 | # dbName: ./data/sqlite/data.db #本地sqlite的名字 3 | app: 4 | publicKey: A8WLqHTjcT/FQ2IWhIePNShUEcdCzu5dG+XrQU8OMu54 5 | privateKey: yScdp6fNgUU+cRUTygvJG4EBhDKmOMRrK4XJ9mKVQJ8= 6 | nodeServer: ws://192.168.99.207:8888/connect/ 7 | my-ip: 192.168.99.207:8080/connect 8 | spring: 9 | datasource: 10 | url: jdbc:sqlite:./data/sqlite/data.db 11 | driver-class-name: org.sqlite.JDBC 12 | type: com.alibaba.druid.pool.DruidDataSource 13 | -------------------------------------------------------------------------------- /src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n 11 | 12 | 13 | 14 | 15 | 16 | 17 | true 18 | 19 | ./logs/logFile.%d{yyyy-MM-dd}.log 20 | 21 | 30 22 | 3GB 23 | 24 | 25 | 26 | %d{yyyy-MM-dd HH:mm:ss} -%msg%n 27 | 28 | 29 | %-4relative [%thread] %-5level %logger{35} - %msg%n 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/test/java/xyz/huanxicloud/blockchainj/BlockchainjApplicationTests.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | import org.springframework.test.context.junit4.SpringRunner; 8 | import xyz.huanxicloud.blockchainj.core.blockchain.BlockChain; 9 | import xyz.huanxicloud.blockchainj.core.blockchain.BlockService; 10 | import xyz.huanxicloud.blockchainj.core.blockchain.block.instuction.InstructionService; 11 | import xyz.huanxicloud.blockchainj.data.dao.RoleMapper; 12 | 13 | import javax.annotation.Resource; 14 | 15 | @RunWith(SpringRunner.class) 16 | @SpringBootTest 17 | public class BlockchainjApplicationTests { 18 | 19 | @Resource 20 | BlockChain blockChain; 21 | @Resource 22 | BlockService blockService; 23 | @Resource 24 | InstructionService instructionService; 25 | 26 | @Autowired 27 | RoleMapper roleMapper; 28 | @Test 29 | public void contextLoads() { 30 | /*Block block = blockService.createBlock(CollectionUtil.newArrayList(instructionService.build("第二次交易"), instructionService.build("第三次交易"))); 31 | blockChain.addBlock(block);*/ 32 | roleMapper.test(); 33 | } 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /src/test/java/xyz/huanxicloud/blockchainj/Test.java: -------------------------------------------------------------------------------- 1 | package xyz.huanxicloud.blockchainj; 2 | 3 | /** 4 | * @author: huanxi 5 | * @date: 2019/1/1 18:01 6 | */ 7 | public class Test { 8 | 9 | public String publicKey = "test"; 10 | 11 | @org.junit.Test 12 | public void test() { 13 | 14 | } 15 | } 16 | --------------------------------------------------------------------------------