├── settings.gradle ├── img └── java-logo.png ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── scripts └── deploy-artifactory.sh ├── .idea ├── codeStyles │ ├── codeStyleConfig.xml │ └── Project.xml └── vcs.xml ├── eosiojavasoftkeysignatureprovider ├── src │ ├── main │ │ └── java │ │ │ └── one │ │ │ └── block │ │ │ └── eosiosoftkeysignatureprovider │ │ │ ├── package-info.java │ │ │ ├── error │ │ │ ├── package-info.java │ │ │ ├── ImportKeyError.java │ │ │ └── SoftKeySignatureErrorConstants.java │ │ │ └── SoftKeySignatureProviderImpl.java │ └── test │ │ └── java │ │ └── one │ │ └── block │ │ └── eosiojavasoftkeysignatureprovider │ │ └── SoftKeySignatureProviderImplTest.java └── build.gradle ├── LICENSE ├── .travis.yml ├── gradlew.bat ├── .gitignore ├── gradlew ├── README.md └── CONTRIBUTING.md /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':eosiojavasoftkeysignatureprovider' 2 | 3 | -------------------------------------------------------------------------------- /img/java-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EOSIO/eosio-java-softkey-signature-provider/HEAD/img/java-logo.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EOSIO/eosio-java-softkey-signature-provider/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /scripts/deploy-artifactory.sh: -------------------------------------------------------------------------------- 1 | DEST_REPO=$1 && \ 2 | echo "publishing to $DEST_REPO on Artifactory" && \ 3 | ./gradlew clean artifactoryPublish -Partifactory_repo=$DEST_REPO -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /eosiojavasoftkeysignatureprovider/src/main/java/one/block/eosiosoftkeysignatureprovider/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Provides the implementation class that implements ISignatureProvider. 3 | */ 4 | package one.block.eosiosoftkeysignatureprovider; -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Aug 21 09:21:31 EDT 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip 7 | -------------------------------------------------------------------------------- /eosiojavasoftkeysignatureprovider/src/main/java/one/block/eosiosoftkeysignatureprovider/error/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Provides the classes necessary to describe meaningful exceptions that occur during a 3 | * signature provider implementation. Individual error classes like {@link one.block.eosiosoftkeysignatureprovider.error.ImportKeyError} 4 | * and error messages defined in a constants file {@link one.block.eosiosoftkeysignatureprovider.error.SoftKeySignatureErrorConstants} 5 | * are included in this package. 6 | */ 7 | package one.block.eosiosoftkeysignatureprovider.error; -------------------------------------------------------------------------------- /eosiojavasoftkeysignatureprovider/src/main/java/one/block/eosiosoftkeysignatureprovider/error/ImportKeyError.java: -------------------------------------------------------------------------------- 1 | package one.block.eosiosoftkeysignatureprovider.error; 2 | 3 | import one.block.eosiojava.error.signatureProvider.SignatureProviderError; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | /** 7 | * Error class is used when there is an exception while attempting to import a key into the 8 | * signature provider. 9 | * 10 | */ 11 | public class ImportKeyError extends SignatureProviderError { 12 | public ImportKeyError() { 13 | } 14 | 15 | public ImportKeyError(@NotNull String message) { 16 | super(message); 17 | } 18 | 19 | public ImportKeyError(@NotNull String message, @NotNull Exception exception) { 20 | super(message, exception); 21 | } 22 | 23 | public ImportKeyError(@NotNull Exception exception) { 24 | super(exception); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017-2019 block.one and its contributors. All rights reserved. 2 | 3 | The MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | env: 3 | global: 4 | - ARTIFACTORY_CONTEXT_URL=https://blockone.jfrog.io/blockone 5 | - ARTIFACTORY_REPO=android-libs-scratch-local 6 | - ARTIFACTORY_LIBS_PATH=https://blockone.jfrog.io/blockone/android-libs 7 | before_cache: 8 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock 9 | - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ 10 | 11 | before_install: 12 | - echo "artifactory_username=$ARTIFACTORY_USERNAME" > gradle.properties 13 | - echo "artifactory_password=$ARTIFACTORY_PASSWORD" >> gradle.properties 14 | - echo "artifactory_path_android_libraries=$ARTIFACTORY_LIBS_PATH" >> gradle.properties 15 | - echo "artifactory_contextURL=$ARTIFACTORY_CONTEXT_URL" >> gradle.properties 16 | - echo "artifactory_repo=$ARTIFACTORY_REPO" >> gradle.properties 17 | 18 | before_deploy: 19 | - echo "artifactory_username=$ARTIFACTORY_USERNAME" > gradle.properties 20 | - echo "artifactory_password=$ARTIFACTORY_PASSWORD" >> gradle.properties 21 | - echo "artifactory_path_android_libraries=$ARTIFACTORY_LIBS_PATH" >> gradle.properties 22 | - echo "artifactory_contextURL=$ARTIFACTORY_CONTEXT_URL" >> gradle.properties 23 | - echo "artifactory_repo=$ARTIFACTORY_REPO" >> gradle.properties 24 | 25 | deploy: 26 | - provider: script 27 | skip_cleanup: true 28 | script: "bash scripts/deploy-artifactory.sh 'android-libs-feature-local'" 29 | on: 30 | all_branches: true 31 | condition: ${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH} =~ ^feature\/.*$ 32 | # develop deploys 33 | - provider: script 34 | skip_cleanup: true 35 | script: "bash scripts/deploy-artifactory.sh 'android-libs-dev-local'" 36 | on: 37 | branch: 38 | - develop 39 | # release branches and master 40 | - provider: script 41 | skip_cleanup: true 42 | script: "bash scripts/deploy-artifactory.sh 'android-libs-release-local'" 43 | on: 44 | all_branches: true 45 | condition: $TRAVIS_BRANCH == master || ${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH} =~ ^release\/.*$ 46 | # tagged releases from master, go to distribution 47 | - provider: script 48 | skip_cleanup: true 49 | script: "bash scripts/deploy-artifactory.sh 'eosiojavasoftkeysignatureprovider-product-eosio-dist'" 50 | on: 51 | tags: true 52 | branch: master 53 | 54 | after_deploy: 55 | - echo "cleaning up properties" 56 | - rm gradle.properties 57 | 58 | 59 | cache: 60 | directories: 61 | - $HOME/.gradle/caches/ 62 | - $HOME/.gradle/wrapper/ -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 2 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 3 | 4 | # User-specific stuff 5 | .idea/**/aws.xml 6 | .idea/**/jarRepositories.xml 7 | .idea/**/workspace.xml 8 | .idea/**/tasks.xml 9 | .idea/**/usage.statistics.xml 10 | .idea/**/dictionaries 11 | .idea/**/shelf 12 | .idea/**/compiler.xml 13 | .idea/**/encodings.xml 14 | .idea/**/misc.xml 15 | .idea/**/vcs.xml 16 | local.properties 17 | 18 | # Generated files 19 | .idea/**/contentModel.xml 20 | .idea/**/caches/ 21 | .idea/**/modules.xml 22 | .idea/**/runConfigurations.xml 23 | 24 | # Sensitive or high-churn files 25 | .idea/**/dataSources/ 26 | .idea/**/dataSources.ids 27 | .idea/**/dataSources.local.xml 28 | .idea/**/sqlDataSources.xml 29 | .idea/**/dynamic.xml 30 | .idea/**/uiDesigner.xml 31 | .idea/**/dbnavigator.xml 32 | 33 | # Gradle 34 | .idea/**/gradle.xml 35 | .idea/**/libraries 36 | 37 | # Gradle and Maven with auto-import 38 | # When using Gradle or Maven with auto-import, you should exclude module files, 39 | # since they will be recreated, and may cause churn. Uncomment if using 40 | # auto-import. 41 | # .idea/modules.xml 42 | # .idea/*.iml 43 | # .idea/modules 44 | *.iml 45 | 46 | # CMake 47 | cmake-build-*/ 48 | 49 | # Mongo Explorer plugin 50 | .idea/**/mongoSettings.xml 51 | 52 | # File-based project format 53 | *.iws 54 | 55 | # IntelliJ 56 | out/ 57 | 58 | # mpeltonen/sbt-idea plugin 59 | .idea_modules/ 60 | 61 | # JIRA plugin 62 | atlassian-ide-plugin.xml 63 | 64 | # Cursive Clojure plugin 65 | .idea/replstate.xml 66 | 67 | # Crashlytics plugin (for Android Studio and IntelliJ) 68 | com_crashlytics_export_strings.xml 69 | crashlytics.properties 70 | crashlytics-build.properties 71 | fabric.properties 72 | 73 | # Editor-based Rest Client 74 | .idea/httpRequests 75 | 76 | # Android studio 3.1+ serialized cache file 77 | .idea/caches/build_file_checksums.ser 78 | 79 | ### Java ### 80 | *.class 81 | 82 | # Mobile Tools for Java (J2ME) 83 | .mtj.tmp/ 84 | 85 | # Package Files # 86 | *.war 87 | *.ear 88 | 89 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 90 | hs_err_pid* 91 | 92 | ### Gradle ### 93 | .gradle 94 | /build/ 95 | /**/build/ 96 | 97 | # Ignore Gradle GUI config 98 | gradle-app.setting 99 | 100 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 101 | !gradle-wrapper.jar 102 | 103 | # Cache of project 104 | .gradletasknamecache 105 | 106 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 107 | # gradle/wrapper/gradle-wrapper.properties 108 | 109 | gradle.properties 110 | -------------------------------------------------------------------------------- /eosiojavasoftkeysignatureprovider/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | //Remove 'com.jfrog.artifactory' plugin if you are not using Artifactory 4 | id 'com.jfrog.artifactory' version '4.9.5' 5 | id 'maven-publish' 6 | 7 | } 8 | 9 | group 'one.block' 10 | 11 | repositories { 12 | //Remove maven{} block if you are not using Artifactory 13 | maven { 14 | credentials{ 15 | username artifactory_username 16 | password artifactory_password 17 | } 18 | url artifactory_path_android_libraries 19 | 20 | } 21 | 22 | //Uncomment if you are not using Artifactory 23 | /* 24 | jcenter() 25 | mavenCentral() 26 | */ 27 | } 28 | 29 | sourceCompatibility = 1.8 30 | targetCompatibility = 1.8 31 | 32 | dependencies { 33 | implementation 'one.block:eosiojava:1.0.0' 34 | testCompile group: 'junit', name: 'junit', version: '4.12' 35 | testCompile 'org.mockito:mockito-core:3.0.0' 36 | testCompile 'org.powermock:powermock-module-junit4:2.0.2' 37 | testCompile 'org.powermock:powermock-api-mockito2:2.0.2' 38 | } 39 | 40 | //Changes module cache control ---- Default is 24 hrs 41 | configurations.all { 42 | resolutionStrategy.cacheChangingModulesFor 1, 'seconds' 43 | } 44 | 45 | def libraryGroupId = 'one.block' 46 | def libraryArtifactId = 'eosiojavasoftkeysignatureprovider' 47 | def libraryVersion = '1.0.0' 48 | 49 | task sourcesJar(type: Jar, dependsOn: classes){ 50 | classifier = 'sources' 51 | from sourceSets.main.allSource 52 | } 53 | 54 | javadoc.failOnError = false 55 | task javadocJar(type: Jar, dependsOn: javadoc){ 56 | classifier = 'javadoc' 57 | from javadoc.destinationDir 58 | } 59 | 60 | artifacts { 61 | archives sourcesJar 62 | archives javadocJar 63 | } 64 | 65 | publishing { 66 | publications { 67 | jar(MavenPublication) { 68 | from components.java 69 | 70 | artifact sourcesJar { 71 | classifier "sources" 72 | } 73 | 74 | artifact javadocJar { 75 | classifier "javadoc" 76 | } 77 | 78 | groupId libraryGroupId 79 | version libraryVersion 80 | artifactId libraryArtifactId 81 | 82 | artifact("$buildDir/libs/${artifactId}.jar") 83 | } 84 | } 85 | } 86 | 87 | //Remove artifactory{} block if you are not using Artifactory 88 | artifactory { 89 | contextUrl = artifactory_contextURL 90 | publish { 91 | repository { 92 | repoKey = artifactory_repo 93 | 94 | username = artifactory_username 95 | password = artifactory_password 96 | } 97 | defaults { 98 | publications('jar') 99 | publishArtifacts = true 100 | 101 | properties = ['qa.level': 'basic', 'q.os': 'android', 'dev.team': 'core'] 102 | publishPom = true 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /eosiojavasoftkeysignatureprovider/src/main/java/one/block/eosiosoftkeysignatureprovider/error/SoftKeySignatureErrorConstants.java: -------------------------------------------------------------------------------- 1 | package one.block.eosiosoftkeysignatureprovider.error; 2 | 3 | import one.block.eosiosoftkeysignatureprovider.SoftKeySignatureProviderImpl; 4 | 5 | /** 6 | * Error constants that pertain to the signing of transactions using the softkey signature provider 7 | * implementation {@link one.block.eosiosoftkeysignatureprovider.SoftKeySignatureProviderImpl} 8 | */ 9 | public class SoftKeySignatureErrorConstants { 10 | /** 11 | * Unable to convert the provided key to PEM format. This probably indicates that the provided 12 | * key is not recognized or invalid. 13 | */ 14 | public static final String IMPORT_KEY_CONVERT_TO_PEM_ERROR = "Can't convert %s to Pem format."; 15 | /** 16 | * No key information was provided. 17 | */ 18 | public static final String IMPORT_KEY_INPUT_EMPTY_ERROR = "Input can't be empty!"; 19 | /** 20 | * PEM conversion produced an empty string. 21 | */ 22 | public static final String CONVERT_TO_PEM_EMPTY_ERROR = "Converting to pem was success but pem result is empty."; 23 | /** 24 | * The signable transaction preparation failed. There may have been a problem with the 25 | * provided serialized transaction. 26 | */ 27 | public static final String SIGN_TRANS_PREPARE_SIGNABLE_TRANS_ERROR = "Error when trying to prepare signable transaction from serialized transaction %s"; 28 | /** 29 | * The list of keys to be used for signing was empty. 30 | */ 31 | public static final String SIGN_TRANS_EMPTY_KEY_LIST = "List of public keys to sign can't be empty!"; 32 | /** 33 | * The provided chain id was empty. 34 | */ 35 | public static final String SIGN_TRANS_EMPTY_CHAIN_ID = "Chain id can't be empty!"; 36 | /** 37 | * The provided serialized transaction was empty. 38 | */ 39 | public static final String SIGN_TRANS_EMPTY_TRANSACTION = "Serialized Transaction can't be empty."; 40 | /** 41 | * No keys have been imported into the signature provider for signing. A key is necessary to 42 | * sign a transaction. 43 | */ 44 | public static final String SIGN_TRANS_NO_KEY_AVAILABLE = "No key available in signature provider! Make sure to call import key."; 45 | /** 46 | * A public key is derived from the private key imported for signing. The public keys needed to verify the transaction that come from 47 | * the chain are compared to the derived public key. If an unexpected exception happens during the matching process, this error message is thrown. 48 | */ 49 | public static final String SIGN_TRANS_SEARCH_KEY_ERROR = "Error when trying to search for corresponding private key from input public key %s"; 50 | /** 51 | * A public key is derived from the private key imported for signing. The public keys needed to verify the transaction that come from 52 | * the chain are compared to the derived public key. If there is not a match this error message is thrown. 53 | */ 54 | public static final String SIGN_TRANS_KEY_NOT_FOUND = "Found no corresponding private key with input public key %s"; 55 | /** 56 | * Encountered when the domain parameters for the key being used to sign the transaction are 57 | * unobtainable. 58 | */ 59 | public static final String SIGN_TRANS_GET_CURVE_DOMAIN_ERROR = "Error when trying to get EC Curve domain of %s"; 60 | /** 61 | * Describes a failure to format the signature that must accompany a signed transaction. 62 | */ 63 | public static final String SIGN_TRANS_FORMAT_SIGNATURE_ERROR = "Error when trying to format signature."; 64 | /** 65 | * Key curve is not supported in key transformation on {@link SoftKeySignatureProviderImpl#getAvailableKeys()} 66 | */ 67 | public static final String GET_KEYS_KEY_FORMAT_NOT_SUPPORTED = "Error on trying to transform key in getAvailableKey(): Algorithm is not supported!"; 68 | } 69 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Java Logo](img/java-logo.png) 2 | # EOSIO SDK for Java: Softkey Signature Provider 3 | 4 | [![Software License](https://img.shields.io/badge/license-MIT-lightgrey.svg)](https://github.com/EOSIO/eosio-java-softkey-signature-provider/blob/master/LICENSE) 5 | ![Language Java](https://img.shields.io/badge/Language-Java-yellow.svg) 6 | ![](https://img.shields.io/badge/Deployment%20Target-JVM-blue.svg) 7 | 8 | Softkey Signature Provider is an example pluggable signature provider for [EOSIO SDK for Java](https://github.com/EOSIO/eosio-java). It allows for signing transactions using in-memory SECP256K1 and SECP256R1 keys. 9 | 10 | **Important:** Softkey Signature Provider stores keys in memory and is therefore not secure. It should only be used for development purposes. In production, we strongly recommend using a signature provider that interfaces with a KeyStore, authenticator or wallet. 11 | 12 | *All product and company names are trademarks™ or registered® trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.* 13 | 14 | ## Contents 15 | 16 | - [Updates](#updates) 17 | - [About Signature Providers](#about-signature-providers) 18 | - [Prerequisites](#prerequisites) 19 | - [Installation](#installation) 20 | - [Basic Usage](#basic-usage) 21 | - [Android Example App](#android-example-app) 22 | - [Library Methods](#library-methods) 23 | - [Want to Help?](#want-to-help) 24 | - [License & Legal](#license) 25 | 26 | ## Updates 27 | - Version 1.0.0. This version consumes the new eosio-java library version 1.0.0. 28 | - Version 0.1.3. The version consumes the new eosio-java library version 0.1.2. 29 | - Version 0.1.2. The version includes updates on Mockito and Powermock dependencies to prevent the build error "Failed to transform...using Jetifier." that is occurring with new versions of AndroidX. 30 | 31 | # About Signature Providers 32 | 33 | The Signature Provider abstraction is arguably the most useful of all of the [EOSIO SDK for Java](https://github.com/EOSIO/eosio-java) providers. It is responsible for: 34 | 35 | * finding out what keys are available for signing (`getAvailableKeys`), and 36 | * requesting and obtaining transaction signatures with a subset of the available keys (`signTransaction`). 37 | 38 | By simply switching out the signature provider on a transaction, signature requests can be routed any number of ways. Need a signature from keys in the platform's Keychain or KeyStore? Configure the [TransactionSession](https://github.com/EOSIO/eosio-java/blob/master/eosiojava/src/main/java/one/block/eosiojava/session/TransactionSession.java) with a conforming signature provider that exposes that functionality. Need signatures from a wallet on the user's device? A signature provider can do that too! 39 | 40 | All signature providers must conform to the [ISignatureProvider](https://github.com/EOSIO/eosio-java/blob/master/eosiojava/src/main/java/one/block/eosiojava/interfaces/ISignatureProvider.java) Protocol. 41 | 42 | ### Prerequisites 43 | 44 | * Java JDK 1.8+ (1.7 source compatibility is targeted) 45 | * Gradle 4.10.1+ 46 | 47 | Since EOSIO SDK for Java: Softkey Signature Provider is not an Android specific project, we recommend using IntelliJ if you are going to work on it. You can use Android Studio but be aware that some of the menu options under Build like `Rebuild Project` and `Clean Project` will not work correctly. You may still compile within Android Studio using `Make Project` under the Build menu, or by using Gradle from the command line. 48 | 49 | ## Installation 50 | 51 | This provider is intended to be used in conjunction with [EOSIO SDK for Java](https://github.com/EOSIO/eosio-java) as a provider plugin. 52 | 53 | To use Softkey Signature Provider with EOSIO SDK for Java in your app, add the following modules to your `build.gradle`: 54 | 55 | ```java 56 | implementation 'one.block:eosiojava:1.0.0' 57 | implementation 'one.block:eosiojavasoftkeysignatureprovider:1.0.0' 58 | ``` 59 | 60 | If you are using Softkey Signature Provider, or any library that depends on it, in an Android application you must also add the following to your application's `build.gradle` file in the `android` section: 61 | 62 | ```groovy 63 | // Needed to get bitcoin-j to produce a valid apk for android. 64 | packagingOptions { 65 | exclude 'lib/x86_64/darwin/libscrypt.dylib' 66 | exclude 'lib/x86_64/freebsd/libscrypt.so' 67 | exclude 'lib/x86_64/linux/libscrypt.so' 68 | } 69 | ``` 70 | The `build.gradle` files for the project currently include configurations for publishing the project to Artifactory. These should be removed if you are not planning to use Artifactory or you will encounter build errors. To do so, make the changes marked by comments throughout the files. 71 | 72 | Then refresh your gradle project. Then you're all set for the [Basic Usage](#basic-usage) example! 73 | 74 | ## Basic Usage 75 | 76 | Generally, signature providers are called by the [TransactionProcessor](https://github.com/EOSIO/eosio-java/blob/master/eosiojava/src/main/java/one/block/eosiojava/session/TransactionProcessor.java) during signing. (See an [example here](https://github.com/EOSIO/eosio-java#basic-usage).) If you find, however, that you need to get available keys or request signing directly, this library can be invoked as follows: 77 | 78 | ```java 79 | SoftKeySignatureProviderImpl provider = new SoftKeySignatureProviderImpl(); 80 | try { 81 | List availableKeys = provider.getAvailableKeys(); 82 | } catch (GetAvailableKeysError getAvailableKeysError) { 83 | getAvailableKeysError.printStackTrace(); 84 | } 85 | ``` 86 | 87 | And to import a private key: 88 | 89 | ```java 90 | try { 91 | provider.importKey("Your eos format private key in SECP256K1 or SECP256R1 type"); 92 | } catch (ImportKeyError importKeyError) { 93 | importKeyError.printStackTrace(); 94 | } 95 | ``` 96 | 97 | To sign an `EosioTransactionSignatureRequest`, you should first create it with your serialized transaction and list of public keys. EOSIO SDK for Java handles the creation of the object for you. 98 | 99 | Finally, call `signTransaction` to sign. 100 | 101 | ```java 102 | try { 103 | String serializedTransaction = "Your serialized transaction"; 104 | List publicKeys = Arrays.asList("Your eos format public key in SECP256K1 or SECP256R1 type"); 105 | EosioTransactionSignatureRequest request = new EosioTransactionSignatureRequest(serializedTransaction, publicKeys, chainId, null, false); 106 | EosioTransactionSignatureResponse response = provider.signTransaction(request); 107 | } catch (SignTransactionError signTransactionError) { 108 | signTransactionError.printStackTrace(); 109 | } 110 | ``` 111 | 112 | ## Android Example App 113 | 114 | If you'd like to see EOSIO SDK for Java: Softkey Signature Provider in action, check out our open source [Android Example App](https://github.com/EOSIO/eosio-java-android-example-app)--a working application that fetches an account's token balance and pushes a transfer action. 115 | 116 | ## Library Methods 117 | 118 | This library is an example implementation of [ISignatureProvider](https://github.com/EOSIO/eosio-java/blob/master/eosiojava/src/main/java/one/block/eosiojava/interfaces/ISignatureProvider.java). It implements the following protocol methods: 119 | 120 | * `signTransaction(EosioTransactionSignatureRequest eosioTransactionSignatureRequest)` signs a `Transaction` 121 | * `getAvailableKeys()` returns an array containing the public keys associated with the private keys that the object is initialized with 122 | 123 | Import a key by calling: 124 | 125 | * `importKey(String privateKey)` 126 | 127 | ## Want to help? 128 | 129 | Interested in contributing? That's awesome! Here are some [Contribution Guidelines](./CONTRIBUTING.md) and the [Code of Conduct](./CONTRIBUTING.md#conduct). 130 | 131 | ## License 132 | 133 | [MIT](./LICENSE) 134 | 135 | ## Important 136 | 137 | See LICENSE for copyright and license terms. Block.one makes its contribution on a voluntary basis as a member of the EOSIO community and is not responsible for ensuring the overall performance of the software or any related applications. We make no representation, warranty, guarantee or undertaking in respect of the software or any related documentation, whether expressed or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall we be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or documentation or the use or other dealings in the software or documentation. Any test results or performance figures are indicative and will not reflect performance under all conditions. Any reference to any third party or third-party product, service or other resource is not an endorsement or recommendation by Block.one. We are not responsible, and disclaim any and all responsibility and liability, for your use of or reliance on any of these resources. Third-party resources may be updated, changed or terminated at any time, so the information here may be out of date or inaccurate. Any person using or offering this software in connection with providing software, goods or services to third parties shall advise such third parties of these license terms, disclaimers and exclusions of liability. Block.one, EOSIO, EOSIO Labs, EOS, the heptahedron and associated logos are trademarks of Block.one. 138 | 139 | Wallets and related components are complex software that require the highest levels of security. If incorrectly built or used, they may compromise users’ private keys and digital assets. Wallet applications and related components should undergo thorough security evaluations before being used. Only experienced developers should work with this software. 140 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to EOSIO SDK for Java: Softkey Signature Provider 2 | 3 | Interested in contributing? That's awesome! Here are some guidelines to get started quickly and easily: 4 | 5 | - [Reporting An Issue](#reporting-an-issue) 6 | - [Bug Reports](#bug-reports) 7 | - [Feature Requests](#feature-requests) 8 | - [Change Requests](#change-requests) 9 | - [Working on Softkey Signature Provider](#working-on-softkey-signature-provider) 10 | - [Feature Branches](#feature-branches) 11 | - [Developing With Gradle Locally](#developing-with-gradle-locally) 12 | - [Submitting Pull Requests](#submitting-pull-requests) 13 | - [Testing and Quality Assurance](#testing-and-quality-assurance) 14 | - [Code Style and Linting](#code-style-and-linting) 15 | - [Conduct](#conduct) 16 | - [Contributor License & Acknowledgments](#contributor-license--acknowledgments) 17 | - [References](#references) 18 | 19 | ## Reporting An Issue 20 | 21 | If you're about to raise an issue because you think you've found a problem with Softkey Signature Provider, or you'd like to make a request for a new feature in the codebase, or any other reason… please read this first. 22 | 23 | The GitHub issue tracker is the preferred channel for [bug reports](#bug-reports), [feature requests](#feature-requests), and [submitting pull requests](#submitting-pull-requests), but please respect the following restrictions: 24 | 25 | * Please **search for existing issues**. Help us keep duplicate issues to a minimum by checking to see if someone has already reported your problem or requested your idea. 26 | 27 | * Please **be civil**. Keep the discussion on topic and respect the opinions of others. See also our [Contributor Code of Conduct](#conduct). 28 | 29 | ### Bug Reports 30 | 31 | A bug is a _demonstrable problem_ that is caused by the code in the repository. Good bug reports are extremely helpful - thank you! 32 | 33 | Guidelines for bug reports: 34 | 35 | 1. **Use the GitHub issue search** — check if the issue has already been 36 | reported. 37 | 38 | 1. **Check if the issue has been fixed** — look for [closed issues in the 39 | current milestone](/../../issues?q=is%3Aissue+is%3Aclosed) or try to reproduce it 40 | using the latest `develop` branch. 41 | 42 | A good bug report shouldn't leave others needing to chase you down for more information. Be sure to include the details of your environment and relevant tests that demonstrate the failure. 43 | 44 | [Report a bug](/../../issues/new?title=Bug%3A) 45 | 46 | ### Feature Requests 47 | 48 | Feature requests are welcome. Before you submit one be sure to have: 49 | 50 | 1. **Use the GitHub search** and check the feature hasn't already been requested. 51 | 1. Take a moment to think about whether your idea fits with the scope and aims of the project. 52 | 1. Remember, it's up to *you* to make a strong case to convince the project's leaders of the merits of this feature. Please provide as much detail and context as possible, this means explaining the use case and why it is likely to be common. 53 | 54 | ### Change Requests 55 | 56 | Change requests cover both architectural and functional changes to how Softkey Signature Provider works. If you have an idea for a new or different dependency, a refactor, or an improvement to a feature, etc - please be sure to: 57 | 58 | 1. **Use the GitHub search** and check someone else didn't get there first 59 | 1. Take a moment to think about the best way to make a case for, and explain what you're thinking. Are you sure this shouldn't really be 60 | a [bug report](#bug-reports) or a [feature request](#feature-requests)? Is it really one idea or is it many? What's the context? What problem are you solving? Why is what you are suggesting better than what's already there? 61 | 62 | ## Working on Softkey Signature Provider 63 | 64 | Code contributions are welcome and encouraged! If you are looking for a good place to start, check out the [good first issue](/../../labels/good%20first%20issue) label in GitHub issues. 65 | 66 | Also, please follow these guidelines when submitting code: 67 | 68 | ### Feature Branches 69 | 70 | To get it out of the way: 71 | 72 | - **[develop](/../../tree/develop)** is the development branch. All work on the next release happens here so you should generally branch off `develop`. Do **NOT** use this branch for a production site. 73 | - **[master](/../../tree/master)** contains the latest release of Softkey Signature Provider. This branch may be used in production. Do **NOT** use this branch to work on Softkey Signature Provider's source. 74 | 75 | ### Developing With Gradle Locally 76 | 77 | By default, libraries are installed from remote Maven repositories through Gradle. If, however, you wish to develop locally and you'd like to integrate with locally-cloned versions of EOSIO SDK for Java and/or other Providers, follow these instructions: 78 | 79 | 1. Clone this and other repos into the same directory, as siblings of one another. 80 | 1. Github repo of all libraries: 81 | * [EOSIO SDK for Java](https://github.com/EOSIO/eosio-java): The core EOSIO SDK for Java library 82 | * [RPC Provider](https://github.com/EOSIO/eosio-java-android-rpc-provider): The RPC provider implementation in the core library 83 | * [ABIEOS Serialization Provider](https://github.com/EOSIO/eosio-java-android-abieos-serialization-provider): A pluggable serialization provider for EOSIO SDK for Java using ABIEOS (for transaction and action conversion between JSON and binary data representations) 84 | * [Softkey Signature Provider](https://github.com/EOSIO/eosio-java-softkey-signature-provider): An example pluggable signature provider for EOSIO SDK for Java for signing transactions using in-memory keys (not for production use) 85 | 1. Import as a gradle project into your favorite IDE or build with gradle from the command line. 86 | 1. Develop! 87 | 88 | ### Submitting Pull Requests 89 | 90 | Pull requests are awesome. If you're looking to raise a PR for something which doesn't have an open issue, please think carefully about [raising an issue](#reporting-an-issue) which your PR can close, especially if you're fixing a bug. This makes it more likely that there will be enough information available for your PR to be properly tested and merged. 91 | 92 | ### Testing and Quality Assurance 93 | 94 | Never underestimate just how useful quality assurance is. If you're looking to get involved with the code base and don't know where to start, checking out and testing a pull request is one of the most useful things you could do. 95 | 96 | Essentially, [check out the latest develop branch](#working-on-softkey-signature-provider), take it for a spin, and if you find anything odd, please follow the [bug report guidelines](#bug-reports) and let us know! 97 | 98 | ### Code Style and Linting 99 | 100 | Softkey Signature Provider leverages [SonarLint](https://www.sonarlint.org/) for linting and the [Google Java Style Guide](https://github.com/google/styleguide) with tab size and indent set to 4, and continuation indent set to 8 for code format flagging. Once SonarLint is installed, linting warnings and errors will be flagged inline with squiggles. Automatic code formatting can be accomplished by downloading and importing the Google Java Style settings into your IDE. 101 | 102 | Please be sure to resolve any linting issues introduced by your contributions prior to requesting a review on your PR. 103 | 104 | ## Conduct 105 | 106 | While contributing, please be respectful and constructive, so that participation in our project is a positive experience for everyone. 107 | 108 | Examples of behavior that contributes to creating a positive environment include: 109 | - Using welcoming and inclusive language 110 | - Being respectful of differing viewpoints and experiences 111 | - Gracefully accepting constructive criticism 112 | - Focusing on what is best for the community 113 | - Showing empathy towards other community members 114 | 115 | Examples of unacceptable behavior include: 116 | - The use of sexualized language or imagery and unwelcome sexual attention or advances 117 | - Trolling, insulting/derogatory comments, and personal or political attacks 118 | - Public or private harassment 119 | - Publishing others’ private information, such as a physical or electronic address, without explicit permission 120 | - Other conduct which could reasonably be considered inappropriate in a professional setting 121 | 122 | ## Contributor License & Acknowledgments 123 | 124 | Whenever you make a contribution to this project, you license your contribution under the same terms as set out in LICENSE, and you represent and warrant that you have the right to license your contribution under those terms. Whenever you make a contribution to this project, you also certify in the terms of the Developer’s Certificate of Origin set out below: 125 | 126 | ``` 127 | Developer Certificate of Origin 128 | Version 1.1 129 | 130 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 131 | 1 Letterman Drive 132 | Suite D4700 133 | San Francisco, CA, 94129 134 | 135 | Everyone is permitted to copy and distribute verbatim copies of this 136 | license document, but changing it is not allowed. 137 | 138 | 139 | Developer's Certificate of Origin 1.1 140 | 141 | By making a contribution to this project, I certify that: 142 | 143 | (a) The contribution was created in whole or in part by me and I 144 | have the right to submit it under the open source license 145 | indicated in the file; or 146 | 147 | (b) The contribution is based upon previous work that, to the best 148 | of my knowledge, is covered under an appropriate open source 149 | license and I have the right under that license to submit that 150 | work with modifications, whether created in whole or in part 151 | by me, under the same open source license (unless I am 152 | permitted to submit under a different license), as indicated 153 | in the file; or 154 | 155 | (c) The contribution was provided directly to me by some other 156 | person who certified (a), (b) or (c) and I have not modified 157 | it. 158 | 159 | (d) I understand and agree that this project and the contribution 160 | are public and that a record of the contribution (including all 161 | personal information I submit with it, including my sign-off) is 162 | maintained indefinitely and may be redistributed consistent with 163 | this project or the open source license(s) involved. 164 | ``` 165 | 166 | ## References 167 | 168 | * Overall CONTRIB adapted from https://github.com/mathjax/MathJax/blob/master/CONTRIBUTING.md 169 | * Conduct section adapted from the Contributor Covenant, version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 170 | -------------------------------------------------------------------------------- /eosiojavasoftkeysignatureprovider/src/main/java/one/block/eosiosoftkeysignatureprovider/SoftKeySignatureProviderImpl.java: -------------------------------------------------------------------------------- 1 | package one.block.eosiosoftkeysignatureprovider; 2 | 3 | 4 | import one.block.eosiojava.enums.AlgorithmEmployed; 5 | import one.block.eosiojava.error.EosioError; 6 | import one.block.eosiojava.error.signatureProvider.GetAvailableKeysError; 7 | import one.block.eosiojava.error.signatureProvider.SignTransactionError; 8 | import one.block.eosiojava.error.utilities.EOSFormatterError; 9 | import one.block.eosiojava.error.utilities.EosFormatterSignatureIsNotCanonicalError; 10 | import one.block.eosiojava.error.utilities.PEMProcessorError; 11 | import one.block.eosiojava.interfaces.ISignatureProvider; 12 | import one.block.eosiojava.models.signatureProvider.EosioTransactionSignatureRequest; 13 | import one.block.eosiojava.models.signatureProvider.EosioTransactionSignatureResponse; 14 | import one.block.eosiojava.utilities.EOSFormatter; 15 | import one.block.eosiojava.utilities.PEMProcessor; 16 | import one.block.eosiosoftkeysignatureprovider.error.ImportKeyError; 17 | import one.block.eosiosoftkeysignatureprovider.error.SoftKeySignatureErrorConstants; 18 | import org.bitcoinj.core.Sha256Hash; 19 | import org.bouncycastle.crypto.params.ECDomainParameters; 20 | import org.bouncycastle.crypto.params.ECPrivateKeyParameters; 21 | import org.bouncycastle.crypto.signers.ECDSASigner; 22 | import org.bouncycastle.util.encoders.Hex; 23 | import org.jetbrains.annotations.NotNull; 24 | 25 | import java.math.BigInteger; 26 | import java.util.ArrayList; 27 | import java.util.LinkedHashSet; 28 | import java.util.List; 29 | import java.util.Set; 30 | 31 | /** 32 | * Example signature provider implementation for EOSIO-java SDK that signs transactions using 33 | * an in-memory private key generated with the secp256r1, prime256v1, or secp256k1 algorithms. This 34 | * implementation is NOT secure and should only be used for educational purposes. It is NOT 35 | * advisable to store private keys outside of secure devices like TEE's and SE's. 36 | */ 37 | public class SoftKeySignatureProviderImpl implements ISignatureProvider { 38 | 39 | /** 40 | * Keep a Set (Unique) of private keys in PEM format 41 | */ 42 | private Set keys = new LinkedHashSet<>(); 43 | 44 | /** 45 | * Maximum number of times the signature provider should try to create a canonical signature 46 | * for a secp256k1 generated key when the attempt fails. 47 | */ 48 | private static final int MAX_NOT_CANONICAL_RE_SIGN = 100; 49 | 50 | /** 51 | * Index of R value in the signature result of softkey signing 52 | */ 53 | private static final int R_INDEX = 0; 54 | 55 | /** 56 | * Index of S value in the signature result of softkey signing 57 | */ 58 | private static final int S_INDEX = 1; 59 | 60 | /** 61 | * Signum to convert a negative value to a positive Big Integer 62 | */ 63 | private static final int BIG_INTEGER_POSITIVE = 1; 64 | 65 | /** 66 | * Flag to indicate getAvailableKeys() should return keys generated with the secp256k1 algorithm in the legacy (prefaced with "EOS") 67 | */ 68 | private static final boolean USING_K1_LEGACY_FORMAT = true; 69 | 70 | /** 71 | * Flag to indicate getAvailableKeys() should return keys generated with the secp256k1 algorithm in the new (prefaced with "PUB_K1_") formats 72 | */ 73 | private static final boolean USING_K1_NON_LEGACY_FORMAT = false; 74 | 75 | /** 76 | * Flag to indicate whether getAvailableKeys() should return keys generated with the secp256k1 algorithm in the legacy (prefaced with "EOS") or new (prefaced with "PUB_K1_") formats. 77 | */ 78 | private static final boolean DEFAULT_WHETHER_USING_K1_LEGACY_FORMAT = USING_K1_NON_LEGACY_FORMAT; 79 | 80 | /** 81 | * Import private key into softkey signature provider. Private key is stored in memory. 82 | * NOT RECOMMENDED for production use!!!! 83 | * 84 | * @param privateKey - Eos format private key 85 | * @throws ImportKeyError Exception that occurs while trying to import a key 86 | */ 87 | public void importKey(@NotNull String privateKey) throws ImportKeyError { 88 | if (privateKey.isEmpty()) { 89 | throw new ImportKeyError(SoftKeySignatureErrorConstants.IMPORT_KEY_INPUT_EMPTY_ERROR); 90 | } 91 | 92 | String privateKeyPem; 93 | 94 | try { 95 | privateKeyPem = EOSFormatter.convertEOSPrivateKeyToPEMFormat(privateKey); 96 | } catch (EOSFormatterError eosFormatterError) { 97 | throw new ImportKeyError(String.format(SoftKeySignatureErrorConstants.IMPORT_KEY_CONVERT_TO_PEM_ERROR, privateKey), eosFormatterError); 98 | } 99 | 100 | if (privateKeyPem.isEmpty()) { 101 | throw new ImportKeyError(SoftKeySignatureErrorConstants.CONVERT_TO_PEM_EMPTY_ERROR); 102 | } 103 | 104 | this.keys.add(privateKeyPem); 105 | } 106 | 107 | @Override 108 | public @NotNull EosioTransactionSignatureResponse signTransaction(@NotNull EosioTransactionSignatureRequest eosioTransactionSignatureRequest) throws SignTransactionError { 109 | 110 | if (eosioTransactionSignatureRequest.getSigningPublicKeys().isEmpty()) { 111 | throw new SignTransactionError(SoftKeySignatureErrorConstants.SIGN_TRANS_EMPTY_KEY_LIST); 112 | 113 | } 114 | 115 | if (eosioTransactionSignatureRequest.getChainId().isEmpty()) { 116 | throw new SignTransactionError(SoftKeySignatureErrorConstants.SIGN_TRANS_EMPTY_CHAIN_ID); 117 | } 118 | 119 | if (eosioTransactionSignatureRequest.getSerializedTransaction().isEmpty()) { 120 | throw new SignTransactionError(SoftKeySignatureErrorConstants.SIGN_TRANS_EMPTY_TRANSACTION); 121 | } 122 | 123 | // Getting serializedTransaction and preparing signable transaction 124 | String serializedTransaction = eosioTransactionSignatureRequest.getSerializedTransaction(); 125 | 126 | // This is the un-hashed message which is used to recover public key 127 | byte[] message; 128 | 129 | // This is the hashed message which is signed. 130 | byte[] hashedMessage; 131 | 132 | try { 133 | message = Hex.decode(EOSFormatter.prepareSerializedTransactionForSigning(serializedTransaction, eosioTransactionSignatureRequest.getChainId()).toUpperCase()); 134 | hashedMessage = Sha256Hash.hash(message); 135 | } catch (EOSFormatterError eosFormatterError) { 136 | throw new SignTransactionError(String.format(SoftKeySignatureErrorConstants.SIGN_TRANS_PREPARE_SIGNABLE_TRANS_ERROR, serializedTransaction), eosFormatterError); 137 | } 138 | 139 | if (this.keys.isEmpty()) { 140 | throw new SignTransactionError(SoftKeySignatureErrorConstants.SIGN_TRANS_NO_KEY_AVAILABLE); 141 | } 142 | 143 | List signatures = new ArrayList<>(); 144 | 145 | // Getting public key and search for the corresponding private key 146 | for (String inputPublicKey : eosioTransactionSignatureRequest.getSigningPublicKeys()) { 147 | 148 | BigInteger privateKeyBI = BigInteger.ZERO; 149 | AlgorithmEmployed curve = null; 150 | 151 | try { 152 | // Search for corresponding private key 153 | for (String key : keys) { 154 | PEMProcessor availableKeyProcessor = new PEMProcessor(key); 155 | //Extract public key in PEM format from inner private key 156 | String innerPublicKeyPEM = availableKeyProcessor.extractPEMPublicKeyFromPrivateKey(DEFAULT_WHETHER_USING_K1_LEGACY_FORMAT); 157 | 158 | // Convert input public key to PEM format for comparision 159 | String inputPublicKeyPEM = EOSFormatter.convertEOSPublicKeyToPEMFormat(inputPublicKey); 160 | 161 | if (innerPublicKeyPEM.equals(inputPublicKeyPEM)) { 162 | privateKeyBI = new BigInteger(BIG_INTEGER_POSITIVE, availableKeyProcessor.getKeyData()); 163 | curve = availableKeyProcessor.getAlgorithm(); 164 | break; 165 | } 166 | } 167 | } catch (EosioError error) { 168 | throw new SignTransactionError(String.format(SoftKeySignatureErrorConstants.SIGN_TRANS_SEARCH_KEY_ERROR, inputPublicKey), error); 169 | } 170 | 171 | // Throw error if found no private key with input public key 172 | //noinspection ConstantConditions 173 | if (privateKeyBI.equals(BigInteger.ZERO) || curve == null) { 174 | throw new SignTransactionError(String.format(SoftKeySignatureErrorConstants.SIGN_TRANS_KEY_NOT_FOUND, inputPublicKey)); 175 | } 176 | 177 | for (int i = 0; i < MAX_NOT_CANONICAL_RE_SIGN; i++) { 178 | // Sign transaction 179 | // Use default constructor to have signature generated with secureRandom, otherwise it would generate same signature for same key all the time 180 | ECDSASigner signer = new ECDSASigner(); 181 | 182 | ECDomainParameters domainParameters; 183 | try { 184 | domainParameters = PEMProcessor.getCurveDomainParameters(curve); 185 | } catch (PEMProcessorError processorError) { 186 | throw new SignTransactionError(String.format(SoftKeySignatureErrorConstants.SIGN_TRANS_GET_CURVE_DOMAIN_ERROR, curve.getString()), processorError); 187 | } 188 | 189 | ECPrivateKeyParameters parameters = new ECPrivateKeyParameters(privateKeyBI, domainParameters); 190 | signer.init(true, parameters); 191 | BigInteger[] signatureComponents = signer.generateSignature(hashedMessage); 192 | 193 | try { 194 | String signature = EOSFormatter.convertRawRandSofSignatureToEOSFormat(signatureComponents[R_INDEX].toString(), signatureComponents[S_INDEX].toString(), message, EOSFormatter.convertEOSPublicKeyToPEMFormat(inputPublicKey)); 195 | // Format Signature 196 | signatures.add(signature); 197 | break; 198 | } catch (EOSFormatterError eosFormatterError) { 199 | // In theory, Non-canonical error only happened with K1 key 200 | if (eosFormatterError.getCause() instanceof EosFormatterSignatureIsNotCanonicalError && curve == AlgorithmEmployed.SECP256K1) { 201 | // Try to sign again until MAX_NOT_CANONICAL_RE_SIGN is reached or get a canonical signature 202 | continue; 203 | } 204 | 205 | throw new SignTransactionError(SoftKeySignatureErrorConstants.SIGN_TRANS_FORMAT_SIGNATURE_ERROR, eosFormatterError); 206 | } 207 | } 208 | } 209 | 210 | return new EosioTransactionSignatureResponse(serializedTransaction, signatures, null); 211 | } 212 | 213 | /** 214 | * Gets available keys from signature provider
Check createSignatureRequest() flow in 215 | * "complete workflow" for more detail of how the method is used. 216 | *

217 | * Public key of SECP256K1 has 2 types of format in EOSIO which are "EOS" and "PUB_K1_" so this method return 2 public keys in both format for SECP256K1 and 1 public key for SECP256R1. 218 | * 219 | * @return the available keys of signature provider in EOS format 220 | * @throws GetAvailableKeysError thrown if there are any exceptions during the get available keys process. 221 | */ 222 | @Override 223 | public @NotNull List getAvailableKeys() throws GetAvailableKeysError { 224 | List availableKeys = new ArrayList<>(); 225 | if (this.keys.isEmpty()) { 226 | return availableKeys; 227 | } 228 | 229 | try { 230 | for (String key : this.keys) { 231 | PEMProcessor processor = new PEMProcessor(key); 232 | AlgorithmEmployed curve = processor.getAlgorithm(); 233 | 234 | switch (curve) { 235 | case SECP256R1: 236 | // USING_K1_NON_LEGACY_FORMAT is being used here because its value does not matter to SECP256R1 key 237 | availableKeys.add(processor.extractEOSPublicKeyFromPrivateKey(USING_K1_NON_LEGACY_FORMAT)); 238 | break; 239 | 240 | case SECP256K1: 241 | // Non legacy 242 | availableKeys.add(processor.extractEOSPublicKeyFromPrivateKey(USING_K1_NON_LEGACY_FORMAT)); 243 | // legacy 244 | availableKeys.add(processor.extractEOSPublicKeyFromPrivateKey(USING_K1_LEGACY_FORMAT)); 245 | break; 246 | 247 | default: 248 | throw new GetAvailableKeysError(SoftKeySignatureErrorConstants.GET_KEYS_KEY_FORMAT_NOT_SUPPORTED); 249 | } 250 | } 251 | } catch (PEMProcessorError pemProcessorError) { 252 | throw new GetAvailableKeysError(SoftKeySignatureErrorConstants.CONVERT_TO_PEM_EMPTY_ERROR, pemProcessorError); 253 | } 254 | 255 | return availableKeys; 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 |

153 | 154 | 155 | 156 | xmlns:android 157 | 158 | ^$ 159 | 160 | 161 | 162 |
163 |
164 | 165 | 166 | 167 | xmlns:.* 168 | 169 | ^$ 170 | 171 | 172 | BY_NAME 173 | 174 |
175 |
176 | 177 | 178 | 179 | .*:id 180 | 181 | http://schemas.android.com/apk/res/android 182 | 183 | 184 | 185 |
186 |
187 | 188 | 189 | 190 | style 191 | 192 | ^$ 193 | 194 | 195 | 196 |
197 |
198 | 199 | 200 | 201 | .* 202 | 203 | ^$ 204 | 205 | 206 | BY_NAME 207 | 208 |
209 |
210 | 211 | 212 | 213 | .*:.*Style 214 | 215 | http://schemas.android.com/apk/res/android 216 | 217 | 218 | BY_NAME 219 | 220 |
221 |
222 | 223 | 224 | 225 | .*:layout_width 226 | 227 | http://schemas.android.com/apk/res/android 228 | 229 | 230 | 231 |
232 |
233 | 234 | 235 | 236 | .*:layout_height 237 | 238 | http://schemas.android.com/apk/res/android 239 | 240 | 241 | 242 |
243 |
244 | 245 | 246 | 247 | .*:layout_weight 248 | 249 | http://schemas.android.com/apk/res/android 250 | 251 | 252 | 253 |
254 |
255 | 256 | 257 | 258 | .*:layout_margin 259 | 260 | http://schemas.android.com/apk/res/android 261 | 262 | 263 | 264 |
265 |
266 | 267 | 268 | 269 | .*:layout_marginTop 270 | 271 | http://schemas.android.com/apk/res/android 272 | 273 | 274 | 275 |
276 |
277 | 278 | 279 | 280 | .*:layout_marginBottom 281 | 282 | http://schemas.android.com/apk/res/android 283 | 284 | 285 | 286 |
287 |
288 | 289 | 290 | 291 | .*:layout_marginStart 292 | 293 | http://schemas.android.com/apk/res/android 294 | 295 | 296 | 297 |
298 |
299 | 300 | 301 | 302 | .*:layout_marginEnd 303 | 304 | http://schemas.android.com/apk/res/android 305 | 306 | 307 | 308 |
309 |
310 | 311 | 312 | 313 | .*:layout_marginLeft 314 | 315 | http://schemas.android.com/apk/res/android 316 | 317 | 318 | 319 |
320 |
321 | 322 | 323 | 324 | .*:layout_marginRight 325 | 326 | http://schemas.android.com/apk/res/android 327 | 328 | 329 | 330 |
331 |
332 | 333 | 334 | 335 | .*:layout_.* 336 | 337 | http://schemas.android.com/apk/res/android 338 | 339 | 340 | BY_NAME 341 | 342 |
343 |
344 | 345 | 346 | 347 | .*:padding 348 | 349 | http://schemas.android.com/apk/res/android 350 | 351 | 352 | 353 |
354 |
355 | 356 | 357 | 358 | .*:paddingTop 359 | 360 | http://schemas.android.com/apk/res/android 361 | 362 | 363 | 364 |
365 |
366 | 367 | 368 | 369 | .*:paddingBottom 370 | 371 | http://schemas.android.com/apk/res/android 372 | 373 | 374 | 375 |
376 |
377 | 378 | 379 | 380 | .*:paddingStart 381 | 382 | http://schemas.android.com/apk/res/android 383 | 384 | 385 | 386 |
387 |
388 | 389 | 390 | 391 | .*:paddingEnd 392 | 393 | http://schemas.android.com/apk/res/android 394 | 395 | 396 | 397 |
398 |
399 | 400 | 401 | 402 | .*:paddingLeft 403 | 404 | http://schemas.android.com/apk/res/android 405 | 406 | 407 | 408 |
409 |
410 | 411 | 412 | 413 | .*:paddingRight 414 | 415 | http://schemas.android.com/apk/res/android 416 | 417 | 418 | 419 |
420 |
421 | 422 | 423 | 424 | .* 425 | http://schemas.android.com/apk/res/android 426 | 427 | 428 | BY_NAME 429 | 430 |
431 |
432 | 433 | 434 | 435 | .* 436 | http://schemas.android.com/apk/res-auto 437 | 438 | 439 | BY_NAME 440 | 441 |
442 |
443 | 444 | 445 | 446 | .* 447 | http://schemas.android.com/tools 448 | 449 | 450 | BY_NAME 451 | 452 |
453 |
454 | 455 | 456 | 457 | .* 458 | .* 459 | 460 | 461 | BY_NAME 462 | 463 |
464 | 465 | 466 | 467 | 468 | 475 | 476 | -------------------------------------------------------------------------------- /eosiojavasoftkeysignatureprovider/src/test/java/one/block/eosiojavasoftkeysignatureprovider/SoftKeySignatureProviderImplTest.java: -------------------------------------------------------------------------------- 1 | package one.block.eosiojavasoftkeysignatureprovider; 2 | 3 | import one.block.eosiojava.enums.AlgorithmEmployed; 4 | import one.block.eosiojava.error.EosioError; 5 | import one.block.eosiojava.error.signatureProvider.GetAvailableKeysError; 6 | import one.block.eosiojava.error.signatureProvider.SignTransactionError; 7 | import one.block.eosiojava.error.utilities.Base58ManipulationError; 8 | import one.block.eosiojava.error.utilities.EOSFormatterError; 9 | import one.block.eosiojava.error.utilities.PEMProcessorError; 10 | import one.block.eosiojava.models.signatureProvider.EosioTransactionSignatureRequest; 11 | import one.block.eosiojava.models.signatureProvider.EosioTransactionSignatureResponse; 12 | import one.block.eosiojava.utilities.EOSFormatter; 13 | import one.block.eosiosoftkeysignatureprovider.SoftKeySignatureProviderImpl; 14 | import one.block.eosiosoftkeysignatureprovider.error.ImportKeyError; 15 | import one.block.eosiosoftkeysignatureprovider.error.SoftKeySignatureErrorConstants; 16 | import org.hamcrest.core.IsInstanceOf; 17 | import org.junit.Rule; 18 | import org.junit.Test; 19 | import org.junit.rules.ExpectedException; 20 | import org.junit.runner.RunWith; 21 | import org.powermock.api.mockito.PowerMockito; 22 | import org.powermock.core.classloader.annotations.PrepareForTest; 23 | import org.powermock.modules.junit4.PowerMockRunner; 24 | 25 | import java.util.ArrayList; 26 | import java.util.Arrays; 27 | import java.util.Collections; 28 | import java.util.List; 29 | 30 | import static org.junit.Assert.*; 31 | import static org.mockito.ArgumentMatchers.any; 32 | import static org.mockito.ArgumentMatchers.anyBoolean; 33 | import static org.powermock.api.mockito.PowerMockito.when; 34 | 35 | /** 36 | * This is a placeholder test class until development starts. 37 | */ 38 | @RunWith(PowerMockRunner.class) 39 | @PrepareForTest(EOSFormatter.class) 40 | public class SoftKeySignatureProviderImplTest { 41 | 42 | @Rule 43 | public ExpectedException exceptionRule = ExpectedException.none(); 44 | 45 | //region positive test 46 | @Test 47 | public void importKeyR1Test() { 48 | String privateKeyEOS = "PVT_R1_g6vV9tiGqN3LkhD53pVUbxDn76PuVeR6XfmJzrnLR3PbGWLys"; 49 | String publicKeyEOS = "PUB_R1_71AYFp3Aasa2od6bwmXEQ13MMfqv4wuJwCRx1Z1dbRifrQEqZt"; 50 | 51 | SoftKeySignatureProviderImpl provider = new SoftKeySignatureProviderImpl(); 52 | 53 | try { 54 | provider.importKey(privateKeyEOS); 55 | List keys = provider.getAvailableKeys(); 56 | assertEquals(1, keys.size()); 57 | assertEquals(publicKeyEOS, keys.get(0)); 58 | } catch (ImportKeyError importKeyError) { 59 | importKeyError.printStackTrace(); 60 | fail("Should not throw error!!!!"); 61 | } catch (GetAvailableKeysError getAvailableKeysError) { 62 | getAvailableKeysError.printStackTrace(); 63 | fail("Should not throw error!!!"); 64 | } 65 | } 66 | 67 | @Test 68 | public void importKeyK1Test() { 69 | String privateKeyEOS = "5JKVeYzRs42DpnHU1rUeJHPZyXb1pCdhyayx7FD2qKHV63F71zU"; 70 | String publicKeyEOSLegacy = "EOS8CbY5PhQZGF2gzPKRBaNG4YzB4AwpmfnDcVZMSPZTqQMiGxPbF"; 71 | String publicKeyEOS = "PUB_K1_8CbY5PhQZGF2gzPKRBaNG4YzB4AwpmfnDcVZMSPZTqQMn1uFhB"; 72 | SoftKeySignatureProviderImpl provider = new SoftKeySignatureProviderImpl(); 73 | 74 | try { 75 | provider.importKey(privateKeyEOS); 76 | List keys = provider.getAvailableKeys(); 77 | assertEquals(2, keys.size()); 78 | assertTrue(keys.contains(publicKeyEOS)); 79 | assertTrue(keys.contains(publicKeyEOSLegacy)); 80 | } catch (ImportKeyError importKeyError) { 81 | importKeyError.printStackTrace(); 82 | fail("Should not throw error!!!!"); 83 | } catch (GetAvailableKeysError getAvailableKeysError) { 84 | getAvailableKeysError.printStackTrace(); 85 | fail("Should not throw error!!!"); 86 | } 87 | } 88 | 89 | @Test 90 | public void getAvailableKeyTest() { 91 | String privateKeyK1EOS = "5JKVeYzRs42DpnHU1rUeJHPZyXb1pCdhyayx7FD2qKHV63F71zU"; 92 | String privateKeyR1EOS = "PVT_R1_g6vV9tiGqN3LkhD53pVUbxDn76PuVeR6XfmJzrnLR3PbGWLys"; 93 | String publicKeyR1EOS = "PUB_R1_71AYFp3Aasa2od6bwmXEQ13MMfqv4wuJwCRx1Z1dbRifrQEqZt"; 94 | String publicKeyK1EOS = "PUB_K1_8CbY5PhQZGF2gzPKRBaNG4YzB4AwpmfnDcVZMSPZTqQMn1uFhB"; 95 | String publicKeyK1EOSLegacy = "EOS8CbY5PhQZGF2gzPKRBaNG4YzB4AwpmfnDcVZMSPZTqQMiGxPbF"; 96 | 97 | SoftKeySignatureProviderImpl provider = new SoftKeySignatureProviderImpl(); 98 | 99 | try { 100 | provider.importKey(privateKeyK1EOS); 101 | provider.importKey(privateKeyR1EOS); 102 | } catch (ImportKeyError importKeyError) { 103 | importKeyError.printStackTrace(); 104 | fail("Should not throw error!!!"); 105 | } 106 | 107 | try { 108 | List keys = provider.getAvailableKeys(); 109 | assertEquals(3, keys.size()); 110 | assertTrue(keys.contains(publicKeyK1EOS)); 111 | assertTrue(keys.contains(publicKeyK1EOSLegacy)); 112 | assertTrue(keys.contains(publicKeyR1EOS)); 113 | } catch (GetAvailableKeysError getAvailableKeysError) { 114 | getAvailableKeysError.printStackTrace(); 115 | fail("Should not throw error!!!"); 116 | } 117 | } 118 | 119 | @Test 120 | public void signTransactionTest() { 121 | String privateKeyR1EOS = "PVT_R1_g6vV9tiGqN3LkhD53pVUbxDn76PuVeR6XfmJzrnLR3PbGWLys"; 122 | String publicKeyR1EOS = "PUB_R1_71AYFp3Aasa2od6bwmXEQ13MMfqv4wuJwCRx1Z1dbRifrQEqZt"; 123 | 124 | String serializedTransaction = "8BC2A35CF56E6CC25F7F000000000100A6823403EA3055000000572D3CCDCD01000000000000C03400000000A8ED32322A000000000000C034000000000000A682A08601000000000004454F530000000009536F6D657468696E6700"; 125 | List publicKeys = Collections.singletonList(publicKeyR1EOS); 126 | String chainId = "687fa513e18843ad3e820744f4ffcf93b1354036d80737db8dc444fe4b15ad17"; 127 | EosioTransactionSignatureRequest request = new EosioTransactionSignatureRequest(serializedTransaction, publicKeys, chainId, null, false); 128 | SoftKeySignatureProviderImpl provider = new SoftKeySignatureProviderImpl(); 129 | 130 | try { 131 | provider.importKey(privateKeyR1EOS); 132 | } catch (ImportKeyError importKeyError) { 133 | importKeyError.printStackTrace(); 134 | fail("Should not fail here!!!"); 135 | } 136 | 137 | try { 138 | EosioTransactionSignatureResponse response = provider.signTransaction(request); 139 | assertNotNull(response); 140 | assertEquals(serializedTransaction, response.getSerializedTransaction()); 141 | assertEquals(1, request.getSigningPublicKeys().size()); 142 | assertTrue(response.getSignatures().get(0).contains("SIG_R1_")); 143 | } catch (SignTransactionError signTransactionError) { 144 | signTransactionError.printStackTrace(); 145 | fail("Should not fail here!!!"); 146 | } 147 | } 148 | 149 | @Test 150 | public void signTransactionTestMultipleKeys() { 151 | // R1 key test 152 | List keyPairs = new ArrayList<>(); 153 | keyPairs.add(new String[]{"PVT_R1_27XFnmZj3gYEeeSy4fy5PoqoP63n94nhTAyFMcuJGAV9b5cqyC", "PUB_R1_5AvUuRssyb7Z2HgNHVofX5heUV5dk8Gni1BGNMzMRCGbhdhBbu"}); 154 | keyPairs.add(new String[]{"PVT_R1_GrfEfbv5at9kbeHcGagQmvbFLdm6jqEpgE1wsGbrfbZNjpVgT", "PUB_R1_4ztaVy8L9zbmzTdpfq5GcaFYwGwXTNmN3qW7qcgHMmfUZhpzQQ"}); 155 | keyPairs.add(new String[]{"PVT_R1_wCpPsaY9o8NU9ZsuwaYVQUDkCfj1aWJZGVcmMM6XyYHJVqvqp", "PUB_R1_5xawnnr3mWayv2wkiqBGWqu4RQLNJffLSXHiL3BofdY7ortMy4"}); 156 | keyPairs.add(new String[]{"PVT_R1_2sXhBwN8hCLSWRxxfZg6hqwGymKSudtQ7Qa5wUWyuW54E1Gd7P", "PUB_R1_6UYnNnXv2CutCtTLgCQxJbHBeWDG3JZaSQJK9tQ7K3JUdzXw9p"}); 157 | keyPairs.add(new String[]{"PVT_R1_2fJmPgaik4rUeU1NDchQjnSPkQkga4iKzdK5hhdbKf2PQFJ57t", "PUB_R1_5MVdX3uzs6qDHUYpdSksZFc5rAu5P4ba6MDaySuYyzQqmCw96Q"}); 158 | keyPairs.add(new String[]{"PVT_R1_2FBMJryipxmAeiwFYXvBTRhX1y5tdepDYBjCm4VqBWcsmdy1xD", "PUB_R1_5qjeAbU6mUM4PLRQBw8V4kxuc5pAjnJFpcMrdZmHF6L6uH57dk"}); 159 | keyPairs.add(new String[]{"PVT_R1_2tjkXAnQPi5Jte8H5SihUQDRnJDPTny5hoiWxxeKm7uC1osiet", "PUB_R1_5BpFt4f1PXzvU2SVmwZdtCiFWbwDRHPzh8Fiao8PCd1R17pH5S"}); 160 | keyPairs.add(new String[]{"PVT_R1_onDM2GMv8D9E7tXuZtGtyEGdLr5TWBuE6weLBwB9hC3NNao6", "PUB_R1_85V3FDScTPvPKhQLQMhrqrQE4xnrqWbZVor5B2LC1qwuJaL15p"}); 161 | keyPairs.add(new String[]{"PVT_R1_jW6MUBQWWmy8Zj1nhtGuMSZaPMH2FXwyJYBkJKndPBRCFPAiH", "PUB_R1_8H8Gwa6rwETdZsKWV6r8Ec9MCa4eVU6PKjDWxShB4EjxRtw5mb"}); 162 | keyPairs.add(new String[]{"PVT_R1_2fX6RwwREC8mVCvX31C5ivCsVAGbQ4waX3wEmiRQoamsGQm2cV", "PUB_R1_5tTLGveJ7TndwDEk8mbw6wEYxNoRmUSrSLjqRdahZWmF1r93QP"}); 163 | keyPairs.add(new String[]{"PVT_R1_2bmerckTyLpK5Z9gaoAHbHWhRBHEC3b8uxdqnFciK9tgx7RnL1", "PUB_R1_84ra3upayTtazvWHJQUgfcJ6hTKYtyUJfckx8qXeRi2kRoxbSJ"}); 164 | keyPairs.add(new String[]{"PVT_R1_2i3AZiJEQxUejHL5c8TheCXdB4yuRacJkVkdR5PxLqDwcax6mT", "PUB_R1_8V7dPunExH1bWnD88gduac71tbjW1CSPuaaUVhJwZG6yF544xE"}); 165 | keyPairs.add(new String[]{"PVT_R1_2HXMYeSqWGqPhUbK8FLzfxJzMV9dPT7ZXFJi9Q2DLkSN1dNwzp", "PUB_R1_7QKfB2nBJJPsUDoBBJVRYV9ak1egBH6dDDGPgvT83zUs1AvQfU"}); 166 | 167 | for (String[] keyPair : keyPairs) { 168 | this.signTransactionTestWithArgs(keyPair[0], keyPair[1]); 169 | } 170 | 171 | // K1 test in WIF test 172 | keyPairs = new ArrayList<>(); 173 | keyPairs.add(new String[]{"5KaUJmXMVc2FF1XMRAjCxmh5or2w6awq4SaGft7HfApJLGDroFd", "EOS5aYo7EthRA5XPG72ekWdbkjYPkk8o7ufLdDzoaj8QR9oshZAXW"}); 174 | keyPairs.add(new String[]{"5KaqhmASMEa6NeV6shfhq8AuYQa5r3xrxWvXB4SiSHwxaQeN2m6", "EOS65d2eTiu7TTdJrQ75JXdMxH657zBiT1Mqz4KhX3eyQAgQtpgf1"}); 175 | keyPairs.add(new String[]{"5K1GuwAFjjvFJsiQu2NHYoWWCV16xAxZ1LgUKw8WrF3T5Tg4556", "EOS8K6Jq3CFSgk3zBNAAsvY7a5p4vzyfxDQDprTwzQnUXnLEZBZAt"}); 176 | keyPairs.add(new String[]{"5KRdi3aCwQWSmX4nqNRbhYe6LLJz3xcxvEcWX141qarZGKqKKXQ", "EOS7ABgtG3CDJEPFfxqbvgLy77pxi2Ai3QJtQvrdhnduxEjjNzKVz"}); 177 | keyPairs.add(new String[]{"5KdTC3SHPDC3orkhvrDbDGvnT2NzR5kexkCnu5RkNbv9vaujSfp", "EOS6pr3Mz91u1Yv3Tkt7T7oqrT4w5nCmSuZCoPHwJCwGipoqaHBQ8"}); 178 | keyPairs.add(new String[]{"5K4VhWHSUJnwTaBhEx8LTECS4DewzRnizb2micRHfwwtnWmSMVx", "EOS8ajgEKL7eba36WpAhAiWp9jWxkP7ySzReFPVLkV7vNXKK6WhqA"}); 179 | keyPairs.add(new String[]{"5KFjmNrL2cx2SysfMhdFzGH9F7ERVfc85TogKeF55jS18VErhiA", "EOS7z5Co6Ynggq2ygsLWrn8sQ7kDvYiBTs5mFxWN8HvcxB35wyXUN"}); 180 | 181 | for (String[] keyPair : keyPairs) { 182 | this.signTransactionTestWithArgs(keyPair[0], keyPair[1]); 183 | } 184 | 185 | // K1 in new format test 186 | keyPairs = new ArrayList<>(); 187 | keyPairs.add(new String[]{"5JKVeYzRs42DpnHU1rUeJHPZyXb1pCdhyayx7FD2qKHV63F71zU", "PUB_K1_8CbY5PhQZGF2gzPKRBaNG4YzB4AwpmfnDcVZMSPZTqQMn1uFhB"}); 188 | keyPairs.add(new String[]{"5KW9dCerdNkGKa5Eis3tqmomFaK3FSwGyzShcFHFCxhSg3cYcda", "PUB_K1_6Lqa1KEfyCtMkf4aTUKsiFiRQ2QsfV6s7MrNe4AHL3xqtMYboc"}); 189 | keyPairs.add(new String[]{"5JfxDRNHhbG9buaYWM9BT2taCckeQo7CzCxBoCrm2GmGSzUegXJ", "PUB_K1_4zLmUratR7P1Bm8ofu1QywPYn9hYTdE4xK5J23n3kwA8jYyqnq"}); 190 | keyPairs.add(new String[]{"5JvqRMk6P7vHoScC3TKzEFBRYGdk1bej6pGGaToEjs9oaZUXm1a", "PUB_K1_5CfaiaeiyWWFXBYnAuYQXfLoTWxsxmycdG7gShvJoQCLRcGTqE"}); 191 | keyPairs.add(new String[]{"5JNMHnmTzKJTDHT5a7JCVU7TRz7As3GdeFdwgWRRgPBNW2buSLY", "PUB_K1_5RGwHXDwKugv8GZEFR1enxNEsoEKGGChwPUtEcEKKQbFmr5jgF"}); 192 | keyPairs.add(new String[]{"5J49cDDhbnwprVysd93yoogQXLh3Q37ge6iJPynjqHbzm9nozGy", "PUB_K1_8SpQXDt2ULK4v3Yx2u7qUp6zNaNT1gRwnrU5AJhM67Lk25tom8"}); 193 | keyPairs.add(new String[]{"5KXUHV94KCG2evdGQ1cNpUJ6PigS99mJBv2mvKNcjxQvXEee3c1", "PUB_K1_7BtR7o48FqfHhXgWtxifXGUPrg94HDHFPgTtgVG1vCA8KaxVgt"}); 194 | keyPairs.add(new String[]{"5K3otXd2RRQTZao5CYQXo2nHu2rC4VRN6LYQwyShxcfwNtsSgLo", "PUB_K1_8mNC41aAcXmZeoMZcRW6DUNSxs7AfZdfLnQ5WMbLPuzKPrk3wL"}); 195 | keyPairs.add(new String[]{"5JNtXpnqYQ1Fe3y7S3D7eVetprBuuf8Xzo89roo1QMPTB24kHkm", "PUB_K1_7FopBLaVh1uUQJ7qxFGCZgMttsBJKhXM9hbpFp83gmrUxYJLLE"}); 196 | keyPairs.add(new String[]{"5JM4fskPtJSwCqSb8Ax6vSYYRr7HwyrpnBxwfG3qr5mUFpMwgkA", "PUB_K1_77DjPHQtPRwXZKDtVvV93PsytUgevbqdPQcZpdPt2MwR541nX2"}); 197 | keyPairs.add(new String[]{"5K8EvWwTbQj7anhLWfr1K1mSMi78CD8YqHquei1gntJDStV8Zdh", "PUB_K1_8B89xwYNkcEEcwyC66LPptRAspCZ7SXy59BqsyoFeNyyq1sKfc"}); 198 | keyPairs.add(new String[]{"5J5DhKFZZqkNkTunWWbgrJNLDwZiK1vJk8Rd8nNL2fKPzLScn1x", "PUB_K1_58aKDkGmkHfswooj8nwhtrc42QrHVzUurnrhDR4Jr1rEk5uXRs"}); 199 | 200 | for (String[] keyPair : keyPairs) { 201 | this.signTransactionTestWithArgs(keyPair[0], keyPair[1]); 202 | } 203 | } 204 | 205 | private void signTransactionTestWithArgs(String privateKey, String publicKey) { 206 | String serializedTransaction = "8BC2A35CF56E6CC25F7F000000000100A6823403EA3055000000572D3CCDCD01000000000000C03400000000A8ED32322A000000000000C034000000000000A682A08601000000000004454F530000000009536F6D657468696E6700"; 207 | List publicKeys = Collections.singletonList(publicKey); 208 | String chainId = "687fa513e18843ad3e820744f4ffcf93b1354036d80737db8dc444fe4b15ad17"; 209 | EosioTransactionSignatureRequest request = new EosioTransactionSignatureRequest(serializedTransaction, publicKeys, chainId, null, false); 210 | SoftKeySignatureProviderImpl provider = new SoftKeySignatureProviderImpl(); 211 | 212 | try { 213 | provider.importKey(privateKey); 214 | } catch (ImportKeyError importKeyError) { 215 | importKeyError.printStackTrace(); 216 | fail("Should not fail here!!!"); 217 | } 218 | 219 | try { 220 | EosioTransactionSignatureResponse response = provider.signTransaction(request); 221 | assertNotNull(response); 222 | assertEquals(serializedTransaction, response.getSerializedTransaction()); 223 | assertEquals(1, request.getSigningPublicKeys().size()); 224 | assertTrue(response.getSignatures().get(0).contains("SIG_")); 225 | } catch (SignTransactionError signTransactionError) { 226 | signTransactionError.printStackTrace(); 227 | fail("Should not fail here!!!"); 228 | } 229 | } 230 | 231 | @Test 232 | public void signTransactionWithMultipleKeyExpectMultiSignatures() { 233 | String privateKeyK1EOS = "5JKVeYzRs42DpnHU1rUeJHPZyXb1pCdhyayx7FD2qKHV63F71zU"; 234 | String publicKeyK1EOS = "PUB_K1_8CbY5PhQZGF2gzPKRBaNG4YzB4AwpmfnDcVZMSPZTqQMn1uFhB"; 235 | String privateKeyR1EOS = "PVT_R1_g6vV9tiGqN3LkhD53pVUbxDn76PuVeR6XfmJzrnLR3PbGWLys"; 236 | String publicKeyR1EOS = "PUB_R1_71AYFp3Aasa2od6bwmXEQ13MMfqv4wuJwCRx1Z1dbRifrQEqZt"; 237 | 238 | String serializedTransaction = "8BC2A35CF56E6CC25F7F000000000100A6823403EA3055000000572D3CCDCD01000000000000C03400000000A8ED32322A000000000000C034000000000000A682A08601000000000004454F530000000009536F6D657468696E6700"; 239 | List publicKeys = Arrays.asList(publicKeyR1EOS, publicKeyK1EOS); 240 | String chainId = "687fa513e18843ad3e820744f4ffcf93b1354036d80737db8dc444fe4b15ad17"; 241 | EosioTransactionSignatureRequest request = new EosioTransactionSignatureRequest(serializedTransaction, publicKeys, chainId, null, false); 242 | SoftKeySignatureProviderImpl provider = new SoftKeySignatureProviderImpl(); 243 | 244 | try { 245 | provider.importKey(privateKeyR1EOS); 246 | provider.importKey(privateKeyK1EOS); 247 | } catch (ImportKeyError importKeyError) { 248 | importKeyError.printStackTrace(); 249 | fail("Should not fail here!!!"); 250 | } 251 | 252 | try { 253 | EosioTransactionSignatureResponse response = provider.signTransaction(request); 254 | assertNotNull(response); 255 | assertEquals(serializedTransaction, response.getSerializedTransaction()); 256 | assertEquals(2, request.getSigningPublicKeys().size()); 257 | assertEquals(request.getSigningPublicKeys().size(), response.getSignatures().size()); 258 | 259 | for (String signature : response.getSignatures()) { 260 | assertTrue(signature.contains("SIG_R1_") || signature.contains("SIG_K1_")); 261 | } 262 | } catch (SignTransactionError signTransactionError) { 263 | signTransactionError.printStackTrace(); 264 | fail("Should not fail here!!!"); 265 | } 266 | } 267 | 268 | //endregion 269 | 270 | //region Negative tests 271 | 272 | @Test 273 | public void importKey_thenFailEmptyError() throws ImportKeyError { 274 | exceptionRule.expect(ImportKeyError.class); 275 | exceptionRule.expectMessage(SoftKeySignatureErrorConstants.IMPORT_KEY_INPUT_EMPTY_ERROR); 276 | new SoftKeySignatureProviderImpl().importKey(""); 277 | } 278 | 279 | @Test 280 | public void importKey_thenFailConvertToPemError() throws ImportKeyError { 281 | String invalidPrivateKey = "trash data"; 282 | exceptionRule.expect(ImportKeyError.class); 283 | exceptionRule.expectMessage(String.format(SoftKeySignatureErrorConstants.IMPORT_KEY_CONVERT_TO_PEM_ERROR, invalidPrivateKey)); 284 | exceptionRule.expectCause(IsInstanceOf.instanceOf(EOSFormatterError.class)); 285 | 286 | new SoftKeySignatureProviderImpl().importKey(invalidPrivateKey); 287 | } 288 | 289 | @Test 290 | public void getAvailableKey_thenFailConvertFromPemToEosError() throws GetAvailableKeysError { 291 | String privateKeyK1EOS = "5JKVeYzRs42DpnHU1rUeJHPZyXb1pCdhyayx7FD2qKHV63F71zU"; 292 | 293 | exceptionRule.expect(GetAvailableKeysError.class); 294 | exceptionRule.expectMessage(SoftKeySignatureErrorConstants.CONVERT_TO_PEM_EMPTY_ERROR); 295 | exceptionRule.expectCause(IsInstanceOf.instanceOf(PEMProcessorError.class)); 296 | 297 | SoftKeySignatureProviderImpl provider = new SoftKeySignatureProviderImpl(); 298 | try { 299 | provider.importKey(privateKeyK1EOS); 300 | } catch (ImportKeyError importKeyError) { 301 | importKeyError.printStackTrace(); 302 | fail("Should not throw exception here"); 303 | } 304 | 305 | try { 306 | PowerMockito.mockStatic(EOSFormatter.class); 307 | when(EOSFormatter.encodePublicKey(any(byte[].class), any(AlgorithmEmployed.class), anyBoolean())).thenThrow(new Base58ManipulationError()); 308 | } catch (Base58ManipulationError base58ManipulationError) { 309 | base58ManipulationError.printStackTrace(); 310 | fail("Should not throw exception on mocking"); 311 | } 312 | 313 | provider.getAvailableKeys(); 314 | } 315 | 316 | @Test 317 | public void signTransactionTest_thenFailEmptyPublicKey() throws SignTransactionError { 318 | exceptionRule.expect(SignTransactionError.class); 319 | exceptionRule.expectMessage(SoftKeySignatureErrorConstants.SIGN_TRANS_EMPTY_KEY_LIST); 320 | 321 | String privateKeyR1EOS = "PVT_R1_g6vV9tiGqN3LkhD53pVUbxDn76PuVeR6XfmJzrnLR3PbGWLys"; 322 | String serializedTransaction = "8BC2A35CF56E6CC25F7F000000000100A6823403EA3055000000572D3CCDCD01000000000000C03400000000A8ED32322A000000000000C034000000000000A682A08601000000000004454F530000000009536F6D657468696E6700"; 323 | List publicKeys = new ArrayList<>(); // Set to empty 324 | String chainId = "687fa513e18843ad3e820744f4ffcf93b1354036d80737db8dc444fe4b15ad17"; 325 | EosioTransactionSignatureRequest request = new EosioTransactionSignatureRequest(serializedTransaction, publicKeys, chainId, null, false); 326 | SoftKeySignatureProviderImpl provider = new SoftKeySignatureProviderImpl(); 327 | 328 | try { 329 | provider.importKey(privateKeyR1EOS); 330 | } catch (ImportKeyError importKeyError) { 331 | importKeyError.printStackTrace(); 332 | fail("Should not fail here!!!"); 333 | } 334 | 335 | provider.signTransaction(request); 336 | } 337 | 338 | @Test 339 | public void signTransactionTest_thenFailWithEmptyChainId() throws SignTransactionError { 340 | exceptionRule.expect(SignTransactionError.class); 341 | exceptionRule.expectMessage(SoftKeySignatureErrorConstants.SIGN_TRANS_EMPTY_CHAIN_ID); 342 | 343 | String privateKeyR1EOS = "PVT_R1_g6vV9tiGqN3LkhD53pVUbxDn76PuVeR6XfmJzrnLR3PbGWLys"; 344 | String publicKeyR1EOS = "PUB_R1_71AYFp3Aasa2od6bwmXEQ13MMfqv4wuJwCRx1Z1dbRifrQEqZt"; 345 | 346 | String serializedTransaction = "8BC2A35CF56E6CC25F7F000000000100A6823403EA3055000000572D3CCDCD01000000000000C03400000000A8ED32322A000000000000C034000000000000A682A08601000000000004454F530000000009536F6D657468696E6700"; 347 | List publicKeys = Collections.singletonList(publicKeyR1EOS); 348 | String chainId = ""; 349 | EosioTransactionSignatureRequest request = new EosioTransactionSignatureRequest(serializedTransaction, publicKeys, chainId, null, false); 350 | SoftKeySignatureProviderImpl provider = new SoftKeySignatureProviderImpl(); 351 | 352 | try { 353 | provider.importKey(privateKeyR1EOS); 354 | } catch (ImportKeyError importKeyError) { 355 | importKeyError.printStackTrace(); 356 | fail("Should not fail here!!!"); 357 | } 358 | 359 | provider.signTransaction(request); 360 | } 361 | 362 | @Test 363 | public void signTransactionTest_thenFailWithEmptySerializedTransaction() throws SignTransactionError { 364 | exceptionRule.expect(SignTransactionError.class); 365 | exceptionRule.expectMessage(SoftKeySignatureErrorConstants.SIGN_TRANS_EMPTY_TRANSACTION); 366 | 367 | String privateKeyR1EOS = "PVT_R1_g6vV9tiGqN3LkhD53pVUbxDn76PuVeR6XfmJzrnLR3PbGWLys"; 368 | String publicKeyR1EOS = "PUB_R1_71AYFp3Aasa2od6bwmXEQ13MMfqv4wuJwCRx1Z1dbRifrQEqZt"; 369 | 370 | String serializedTransaction = ""; 371 | List publicKeys = Collections.singletonList(publicKeyR1EOS); 372 | String chainId = "687fa513e18843ad3e820744f4ffcf93b1354036d80737db8dc444fe4b15ad17"; 373 | EosioTransactionSignatureRequest request = new EosioTransactionSignatureRequest(serializedTransaction, publicKeys, chainId, null, false); 374 | SoftKeySignatureProviderImpl provider = new SoftKeySignatureProviderImpl(); 375 | 376 | try { 377 | provider.importKey(privateKeyR1EOS); 378 | } catch (ImportKeyError importKeyError) { 379 | importKeyError.printStackTrace(); 380 | fail("Should not fail here!!!"); 381 | } 382 | 383 | provider.signTransaction(request); 384 | } 385 | 386 | @Test 387 | public void signTransactionTest_thenFailWithPrepareSignableTransaction() throws SignTransactionError { 388 | String serializedTransaction = "8BC2A35CF56E6CC25F7F000000000100A6823403EA3055000000572D3CCDCD01000000000000C03400000000A8ED32322A000000000000C034000000000000A682A08601000000000004454F530000000009536F6D657468696E6700"; 389 | exceptionRule.expect(SignTransactionError.class); 390 | exceptionRule.expectMessage(String.format(SoftKeySignatureErrorConstants.SIGN_TRANS_PREPARE_SIGNABLE_TRANS_ERROR, serializedTransaction)); 391 | exceptionRule.expectCause(IsInstanceOf.instanceOf(EOSFormatterError.class)); 392 | 393 | String privateKeyR1EOS = "PVT_R1_g6vV9tiGqN3LkhD53pVUbxDn76PuVeR6XfmJzrnLR3PbGWLys"; 394 | String publicKeyR1EOS = "PUB_R1_71AYFp3Aasa2od6bwmXEQ13MMfqv4wuJwCRx1Z1dbRifrQEqZt"; 395 | 396 | List publicKeys = Collections.singletonList(publicKeyR1EOS); 397 | String chainId = "687fa513e18843ad3e820744f4ffcf93b1354036d80737db8dc444fe4b15ad17"; 398 | EosioTransactionSignatureRequest request = new EosioTransactionSignatureRequest(serializedTransaction, publicKeys, chainId, null, false); 399 | SoftKeySignatureProviderImpl provider = new SoftKeySignatureProviderImpl(); 400 | 401 | try { 402 | provider.importKey(privateKeyR1EOS); 403 | } catch (ImportKeyError importKeyError) { 404 | importKeyError.printStackTrace(); 405 | fail("Should not fail here!!!"); 406 | } 407 | 408 | try { 409 | PowerMockito.mockStatic(EOSFormatter.class); 410 | when(EOSFormatter.prepareSerializedTransactionForSigning(any(String.class), any(String.class))).thenThrow(new EOSFormatterError()); 411 | } catch (EOSFormatterError eosFormatterError) { 412 | eosFormatterError.printStackTrace(); 413 | fail("Should not fail here!!!"); 414 | } 415 | provider.signTransaction(request); 416 | } 417 | 418 | @Test 419 | public void signTransactionTest_thenFailNoKeyAvailable() throws SignTransactionError { 420 | exceptionRule.expect(SignTransactionError.class); 421 | exceptionRule.expectMessage(SoftKeySignatureErrorConstants.SIGN_TRANS_NO_KEY_AVAILABLE); 422 | 423 | String publicKeyR1EOS = "PUB_R1_71AYFp3Aasa2od6bwmXEQ13MMfqv4wuJwCRx1Z1dbRifrQEqZt"; 424 | 425 | String serializedTransaction = "8BC2A35CF56E6CC25F7F000000000100A6823403EA3055000000572D3CCDCD01000000000000C03400000000A8ED32322A000000000000C034000000000000A682A08601000000000004454F530000000009536F6D657468696E6700"; 426 | List publicKeys = Collections.singletonList(publicKeyR1EOS); 427 | String chainId = "687fa513e18843ad3e820744f4ffcf93b1354036d80737db8dc444fe4b15ad17"; 428 | EosioTransactionSignatureRequest request = new EosioTransactionSignatureRequest(serializedTransaction, publicKeys, chainId, null, false); 429 | SoftKeySignatureProviderImpl provider = new SoftKeySignatureProviderImpl(); 430 | 431 | provider.signTransaction(request); 432 | } 433 | 434 | @Test 435 | public void signTransactionTest_thenFailKeyNotFound() throws SignTransactionError { 436 | String publicKeyR1EOS = "PUB_R1_71AYFp3Aasa2od6bwmXEQ13MMfqv4wuJwCRx1Z1dbRifrQEqZt"; 437 | 438 | exceptionRule.expect(SignTransactionError.class); 439 | exceptionRule.expectMessage(String.format(SoftKeySignatureErrorConstants.SIGN_TRANS_KEY_NOT_FOUND, publicKeyR1EOS)); 440 | 441 | String privateKeyK1EOS = "5JKVeYzRs42DpnHU1rUeJHPZyXb1pCdhyayx7FD2qKHV63F71zU"; 442 | 443 | String serializedTransaction = "8BC2A35CF56E6CC25F7F000000000100A6823403EA3055000000572D3CCDCD01000000000000C03400000000A8ED32322A000000000000C034000000000000A682A08601000000000004454F530000000009536F6D657468696E6700"; 444 | List publicKeys = Collections.singletonList(publicKeyR1EOS); 445 | String chainId = "687fa513e18843ad3e820744f4ffcf93b1354036d80737db8dc444fe4b15ad17"; 446 | EosioTransactionSignatureRequest request = new EosioTransactionSignatureRequest(serializedTransaction, publicKeys, chainId, null, false); 447 | SoftKeySignatureProviderImpl provider = new SoftKeySignatureProviderImpl(); 448 | 449 | try { 450 | provider.importKey(privateKeyK1EOS); 451 | } catch (ImportKeyError importKeyError) { 452 | importKeyError.printStackTrace(); 453 | fail("Should not fail here!!!"); 454 | } 455 | 456 | provider.signTransaction(request); 457 | } 458 | 459 | @Test 460 | public void getAvailableKeyK1_thenFailKeyAmountIsNot1() { 461 | String privateKeyEOS = "5JKVeYzRs42DpnHU1rUeJHPZyXb1pCdhyayx7FD2qKHV63F71zU"; 462 | SoftKeySignatureProviderImpl provider = new SoftKeySignatureProviderImpl(); 463 | 464 | try { 465 | provider.importKey(privateKeyEOS); 466 | List keys = provider.getAvailableKeys(); 467 | assertNotEquals(1, keys.size()); 468 | } catch (ImportKeyError importKeyError) { 469 | importKeyError.printStackTrace(); 470 | fail("Should not throw error!!!!"); 471 | } catch (GetAvailableKeysError getAvailableKeysError) { 472 | getAvailableKeysError.printStackTrace(); 473 | fail("Should not throw error!!!"); 474 | } 475 | } 476 | 477 | //endregion 478 | } 479 | --------------------------------------------------------------------------------