diff --git a/.gitignore b/.gitignore index 094313f..0790958 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ # Gradle # ------ .gradle +gradlew.bat gradle graphenej/build /build diff --git a/gradle.properties b/gradle.properties index 1c0fbdd..1053dd5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,8 +17,8 @@ # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true -VERSION_NAME=0.4.2-SNAPSHOT -VERSION_CODE=4 +VERSION_NAME=0.4.4 +VERSION_CODE=6 GROUP=com.github.bilthon POM_DESCRIPTION=A Java library for mobile app Developers; Graphene/Bitshares blockchain. diff --git a/gradlew.bat b/gradlew.bat deleted file mode 100644 index 8a0b282..0000000 --- a/gradlew.bat +++ /dev/null @@ -1,90 +0,0 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windowz variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/graphenej/build.gradle b/graphenej/build.gradle index e4badbf..64f87e8 100644 --- a/graphenej/build.gradle +++ b/graphenej/build.gradle @@ -1,5 +1,5 @@ group 'de.bitsharesmunich' -version '0.4.2-SNAPSHOT' +version '0.4.4' apply plugin: 'com.android.library' apply from: 'maven-push.gradle' @@ -19,10 +19,10 @@ android { buildToolsVersion "25.0.0" defaultConfig { - minSdkVersion 3 + minSdkVersion 9 targetSdkVersion 24 - versionCode 4 - versionName "0.4.2" + versionCode 6 + versionName "0.4.4" vectorDrawables.useSupportLibrary = true } diff --git a/graphenej/src/main/AndroidManifest.xml b/graphenej/src/main/AndroidManifest.xml index 441efd0..b86d91f 100644 --- a/graphenej/src/main/AndroidManifest.xml +++ b/graphenej/src/main/AndroidManifest.xml @@ -1,8 +1,8 @@ + android:versionCode="5" + android:versionName="0.4.3" > diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/AuthorityType.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/AuthorityType.java new file mode 100644 index 0000000..ab0faa8 --- /dev/null +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/AuthorityType.java @@ -0,0 +1,13 @@ +package de.bitsharesmunich.graphenej; + +/** + * Enum-type used to specify the different roles of an authority. + * + * @see Authority + */ + +public enum AuthorityType { + OWNER, + ACTIVE, + MEMO +} diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/RPC.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/RPC.java index bf97c50..2375925 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/RPC.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/RPC.java @@ -11,6 +11,7 @@ public class RPC { public static final String CALL_DATABASE = "database"; public static final String CALL_ASSET = "asset"; public static final String CALL_SET_SUBSCRIBE_CALLBACK = "set_subscribe_callback"; + public static final String CALL_CANCEL_ALL_SUBSCRIPTIONS = "cancel_all_subscriptions"; public static final String CALL_GET_ACCOUNT_BY_NAME = "get_account_by_name"; public static final String CALL_GET_ACCOUNTS = "get_accounts"; public static final String CALL_GET_DYNAMIC_GLOBAL_PROPERTIES = "get_dynamic_global_properties"; diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/BaseGrapheneHandler.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/BaseGrapheneHandler.java index a21c1bc..80d7e2b 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/BaseGrapheneHandler.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/BaseGrapheneHandler.java @@ -3,24 +3,57 @@ package de.bitsharesmunich.graphenej.api; import com.neovisionaries.ws.client.WebSocket; import com.neovisionaries.ws.client.WebSocketAdapter; import com.neovisionaries.ws.client.WebSocketException; + +import org.w3c.dom.Node; + +import de.bitsharesmunich.graphenej.interfaces.NodeErrorListener; import de.bitsharesmunich.graphenej.interfaces.WitnessResponseListener; import de.bitsharesmunich.graphenej.models.BaseResponse; /** - * Created by nelson on 1/5/17. + * Base class that should be extended by any implementation of a specific request to the full node. */ public abstract class BaseGrapheneHandler extends WebSocketAdapter { protected WitnessResponseListener mListener; + protected NodeErrorListener mErrorListener; + /** + * The 'id' field of a message to the node. This is used in order to multiplex different messages + * using the same websocket connection. + * + * For example: + * + * {"id":5,"method":"call","params":[0,"get_accounts",[["1.2.100"]]],"jsonrpc":"2.0"} + * + * The 'requestId' here is 5. + */ + protected long requestId; + + /** + * Constructor (The original constructor, should be replaced with the one that receives + * NodeErrorListener instead of WitnessResponseListener) + * + * @param listener listener to be notified in if an error occurs + */ + @Deprecated public BaseGrapheneHandler(WitnessResponseListener listener){ this.mListener = listener; } + /** + * Constructor + * + * @param listener listener to be notified if an error occurs + */ + public BaseGrapheneHandler(NodeErrorListener listener){ + this.mErrorListener = listener; + } + @Override public void onError(WebSocket websocket, WebSocketException cause) throws Exception { System.out.println("onError. cause: "+cause.getMessage()); - mListener.onError(new BaseResponse.Error(cause.getMessage())); + mErrorListener.onError(new BaseResponse.Error(cause.getMessage())); websocket.disconnect(); } @@ -30,7 +63,22 @@ public abstract class BaseGrapheneHandler extends WebSocketAdapter { for (StackTraceElement element : cause.getStackTrace()){ System.out.println(element.getFileName()+"#"+element.getClassName()+":"+element.getLineNumber()); } - mListener.onError(new BaseResponse.Error(cause.getMessage())); + // Should be replaced for mErrorListener (NodeErrorListener type) only in the future + if(mErrorListener != null){ + mErrorListener.onError(new BaseResponse.Error(cause.getMessage())); + } + else{ + mListener.onError(new BaseResponse.Error(cause.getMessage())); + } + websocket.disconnect(); } + + public void setRequestId(long id){ + this.requestId = id; + } + + public long getRequestId(){ + return this.requestId; + } } diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetAccountBalances.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetAccountBalances.java index d7aeda4..41f6287 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetAccountBalances.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetAccountBalances.java @@ -19,17 +19,52 @@ import java.util.List; import java.util.Map; /** - * Created by nelson on 1/13/17. + * Class that implements get_account_balances request handler. + * + * Get an account’s balances in various assets. + * + * The response returns the balances of the account + * + * @see get_account_balances API doc + * */ public class GetAccountBalances extends BaseGrapheneHandler { private UserAccount mUserAccount; private List mAssetList; + private boolean mOneTime; - public GetAccountBalances(UserAccount userAccount, List assets, WitnessResponseListener listener) { + /** + * Default Constructor + * + * @param userAccount account to get balances for + * @param assets list of the assets to get balances of; if empty, get all assets account + * has a balance in + * @param oneTime boolean value indicating if WebSocket must be closed (true) or not + * (false) after the response + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. + */ + public GetAccountBalances(UserAccount userAccount, List assets, boolean oneTime, WitnessResponseListener listener) { super(listener); this.mUserAccount = userAccount; this.mAssetList = assets; + this.mOneTime = oneTime; + } + + /** + * Using this constructor the WebSocket connection closes after the response. + * + * @param userAccount account to get balances for + * @param assets list of the assets to get balances of; if empty, get all assets account + * has a balance in + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. + */ + public GetAccountBalances(UserAccount userAccount, List assets, WitnessResponseListener listener) { + this(userAccount, assets, true, listener); } @Override @@ -57,7 +92,9 @@ public class GetAccountBalances extends BaseGrapheneHandler { Type WitnessResponseType = new TypeToken>>(){}.getType(); WitnessResponse> witnessResponse = gsonBuilder.create().fromJson(response, WitnessResponseType); mListener.onSuccess(witnessResponse); - websocket.disconnect(); + if(mOneTime){ + websocket.disconnect(); + } } @Override diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetAccountByName.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetAccountByName.java index f4f17f8..d11634f 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetAccountByName.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetAccountByName.java @@ -20,19 +20,49 @@ import de.bitsharesmunich.graphenej.models.ApiCall; import de.bitsharesmunich.graphenej.models.WitnessResponse; /** - * Created by nelson on 11/15/16. + * Class that implements get_account_by_name request handler. + * + * Get an account’s info by name. + * + * The response returns account data that refer to the name. + * + * @see get_account_by_name API doc */ public class GetAccountByName extends BaseGrapheneHandler { private String accountName; private WitnessResponseListener mListener; + private boolean mOneTime; - public GetAccountByName(String accountName, WitnessResponseListener listener){ + /** + * Default Constructor + * + * @param accountName name of the account to get info + * @param oneTime boolean value indicating if WebSocket must be closed (true) or not + * (false) after the response + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. + */ + public GetAccountByName(String accountName, boolean oneTime, WitnessResponseListener listener){ super(listener); this.accountName = accountName; + this.mOneTime = oneTime; this.mListener = listener; } + /** + * Using this constructor the WebSocket connection closes after the response. + * + * @param accountName name of the account to get info + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. + */ + public GetAccountByName(String accountName, WitnessResponseListener listener){ + this(accountName, true, listener); + } + @Override public void onConnected(WebSocket websocket, Map> headers) throws Exception { ArrayList accountParams = new ArrayList<>(); @@ -58,8 +88,9 @@ public class GetAccountByName extends BaseGrapheneHandler { }else{ this.mListener.onSuccess(witnessResponse); } - - websocket.disconnect(); + if(mOneTime){ + websocket.disconnect(); + } } @Override diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetAccountHistory.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetAccountHistory.java index 1c1db62..061cedc 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetAccountHistory.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetAccountHistory.java @@ -3,5 +3,6 @@ package de.bitsharesmunich.graphenej.api; /** * Created by nelson on 12/26/16. */ +//TODO: Implement if needed: http://docs.bitshares.eu/api/history.html?highlight=get_market_history#account-history-api public class GetAccountHistory { } diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetAccounts.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetAccounts.java index 11377db..9350ee5 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetAccounts.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetAccounts.java @@ -21,27 +21,79 @@ import de.bitsharesmunich.graphenej.models.ApiCall; import de.bitsharesmunich.graphenej.models.WitnessResponse; /** + * Class that implements get_accounts request handler. * - * @author henry + * Get a list of accounts by ID. + * + * The response returns the accounts corresponding to the provided IDs. + * + * @see get_accounts API doc */ public class GetAccounts extends BaseGrapheneHandler { - private String accountId; private List userAccounts; private WitnessResponseListener mListener; + private boolean mOneTime; - public GetAccounts(String accountId, WitnessResponseListener listener){ + /** + * Constructor for one account only. + * + * @param accountId ID of the account to retrieve + * @param oneTime boolean value indicating if WebSocket must be closed (true) or not + * (false) after the response + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. + */ + public GetAccounts(String accountId, boolean oneTime, WitnessResponseListener listener){ super(listener); this.accountId = accountId; + this.mOneTime = oneTime; this.mListener = listener; } - public GetAccounts(List accounts, WitnessResponseListener listener){ + /** + * Constructor for account list. + * + * @param accounts list with the accounts to retrieve + * @param oneTime boolean value indicating if WebSocket must be closed (true) or not + * (false) after the response + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. + */ + public GetAccounts(List accounts, boolean oneTime, WitnessResponseListener listener){ super(listener); this.userAccounts = accounts; + this.mOneTime = oneTime; this.mListener = listener; } + /** + * Using this constructor the WebSocket connection closes after the response. (Account based) + * + * @param accountId ID of the account to retrieve + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. + */ + public GetAccounts(String accountId, WitnessResponseListener listener){ + this(accountId, true, listener); + } + + /** + * Using this constructor the WebSocket connection closes after the response. (Account List + * based) + * + * @param accounts list with the accounts to retrieve + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. + */ + public GetAccounts(List accounts, WitnessResponseListener listener){ + this(accounts, true, listener); + } + @Override public void onConnected(WebSocket websocket, Map> headers) throws Exception { ArrayList params = new ArrayList(); @@ -54,7 +106,7 @@ public class GetAccounts extends BaseGrapheneHandler { accountIds.add(accountId); } params.add(accountIds); - ApiCall getAccountByAddress = new ApiCall(0, RPC.CALL_GET_ACCOUNTS, params, RPC.VERSION, 1); + ApiCall getAccountByAddress = new ApiCall(0, RPC.CALL_GET_ACCOUNTS, params, RPC.VERSION, (int) requestId); websocket.sendText(getAccountByAddress.toJsonString()); } @@ -74,7 +126,9 @@ public class GetAccounts extends BaseGrapheneHandler { } else { this.mListener.onSuccess(witnessResponse); } - websocket.disconnect(); + if(mOneTime){ + websocket.disconnect(); + } } @Override diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetAllAssetHolders.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetAllAssetHolders.java index 9d92f8b..4147bc5 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetAllAssetHolders.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetAllAssetHolders.java @@ -16,7 +16,13 @@ import java.util.List; import java.util.Map; /** - * Created by nelson on 1/25/17. + * Class that implements get_all_asset_holders request handler. + * + * Get a list of all system assets with holders count. + * + * The response returns the list of all assets with holders count. + * + * @see get_all_asset_holders API doc (source code ref.) */ public class GetAllAssetHolders extends BaseGrapheneHandler { private final static int LOGIN_ID = 1; @@ -26,10 +32,31 @@ public class GetAllAssetHolders extends BaseGrapheneHandler { private int currentId = 1; private int assetApiId = -1; - public GetAllAssetHolders(WitnessResponseListener listener) { + private boolean mOneTime; + + /** + * Default Constructor + * + * @param oneTime boolean value indicating if WebSocket must be closed (true) or not + * (false) after the response + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. + */ + public GetAllAssetHolders(boolean oneTime, WitnessResponseListener listener) { super(listener); + this.mOneTime = oneTime; } + /** + * Using this constructor the WebSocket connection closes after the response. + * + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the success/failure + * of the transaction broadcast operation. + */ + public GetAllAssetHolders(WitnessResponseListener listener) { this(true, listener);} + @Override public void onConnected(WebSocket websocket, Map> headers) throws Exception { ArrayList loginParams = new ArrayList<>(); @@ -48,7 +75,9 @@ public class GetAllAssetHolders extends BaseGrapheneHandler { BaseResponse baseResponse = gson.fromJson(response, BaseResponse.class); if(baseResponse.error != null){ mListener.onError(baseResponse.error); - websocket.disconnect(); + if(mOneTime){ + websocket.disconnect(); + } }else { currentId++; ArrayList emptyParams = new ArrayList<>(); @@ -68,7 +97,9 @@ public class GetAllAssetHolders extends BaseGrapheneHandler { builder.registerTypeAdapter(AssetHolderCount.class, new AssetHolderCount.HoldersCountDeserializer()); WitnessResponse> witnessResponse = builder.create().fromJson(response, AssetTokenHolders); mListener.onSuccess(witnessResponse); - websocket.disconnect(); + if(mOneTime){ + websocket.disconnect(); + } }else{ System.out.println("current id: "+currentId); } diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetBlockHeader.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetBlockHeader.java index 4cab506..cb59103 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetBlockHeader.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetBlockHeader.java @@ -19,7 +19,13 @@ import de.bitsharesmunich.graphenej.models.BlockHeader; import de.bitsharesmunich.graphenej.models.WitnessResponse; /** - * Created by nelson on 12/13/16. + * Class that implements get_block_header request handler. + * + * Retrieve a block header. + * + * The request returns the header of the referenced block, or null if no matching block was found + * + * @see get_block_header API doc */ public class GetBlockHeader extends BaseGrapheneHandler { @@ -34,12 +40,37 @@ public class GetBlockHeader extends BaseGrapheneHandler { private int currentId = LOGIN_ID; private int apiId = -1; - public GetBlockHeader(long blockNumber, WitnessResponseListener listener){ + private boolean mOneTime; + + /** + * Default Constructor + * + * @param blockNumber height of the block whose header should be returned + * @param oneTime boolean value indicating if WebSocket must be closed (true) or not + * (false) after the response + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. + */ + public GetBlockHeader(long blockNumber, boolean oneTime, WitnessResponseListener listener){ super(listener); this.blockNumber = blockNumber; + this.mOneTime = oneTime; this.mListener = listener; } + /** + * Using this constructor the WebSocket connection closes after the response. + * + * @param blockNumber height of the block whose header should be returned + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. + */ + public GetBlockHeader(long blockNumber, WitnessResponseListener listener){ + this(blockNumber, true, listener); + } + @Override public void onConnected(WebSocket websocket, Map> headers) throws Exception { ArrayList loginParams = new ArrayList<>(); @@ -58,7 +89,9 @@ public class GetBlockHeader extends BaseGrapheneHandler { BaseResponse baseResponse = gson.fromJson(response, BaseResponse.class); if(baseResponse.error != null){ mListener.onError(baseResponse.error); - websocket.disconnect(); + if(mOneTime){ + websocket.disconnect(); + } }else { currentId++; ArrayList emptyParams = new ArrayList<>(); @@ -80,7 +113,9 @@ public class GetBlockHeader extends BaseGrapheneHandler { Type RelativeAccountHistoryResponse = new TypeToken>(){}.getType(); WitnessResponse transfersResponse = gson.fromJson(response, RelativeAccountHistoryResponse); mListener.onSuccess(transfersResponse); - websocket.disconnect(); + if(mOneTime){ + websocket.disconnect(); + } } } diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetKeyReferences.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetKeyReferences.java index 5eb4243..576d6a6 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetKeyReferences.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetKeyReferences.java @@ -19,23 +19,75 @@ import de.bitsharesmunich.graphenej.models.ApiCall; import de.bitsharesmunich.graphenej.models.WitnessResponse; /** - * Created by nelson on 11/15/16. + * Class that implements get_key_references request handler. + * + * Retrieve the keys that refer to the address/list of addresses. + * + * The request returns all accounts that refer to the key or account id in their owner or active authorities. + * + * @see get_key_references API doc */ public class GetKeyReferences extends BaseGrapheneHandler { private List
addresses; - public GetKeyReferences(Address address, WitnessResponseListener listener){ + private boolean mOneTime; + + /** + * Constructor + * + * @param address address to be query + * @param oneTime boolean value indicating if WebSocket must be closed (true) or not (false) + * after the response + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the success/failure + * of the transaction broadcast operation. + */ + public GetKeyReferences(Address address, boolean oneTime, WitnessResponseListener listener){ super(listener); addresses = new ArrayList<>(); addresses.add(address); - + this.mOneTime = oneTime; } - public GetKeyReferences(List
addresses, WitnessResponseListener listener) { + /** + * + * @param addresses list of addresses to be query + * @param oneTime boolean value indicating if WebSocket must be closed (true) or not (false) + * after the response + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the success/failure + * of the transaction broadcast operation. + */ + public GetKeyReferences(List
addresses, boolean oneTime, WitnessResponseListener listener) { super(listener); this.addresses = addresses; this.mListener = listener; + this.mOneTime = oneTime; + } + + /** + * Using this constructor the websocket connection closes after the response. + * + * @param address + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the success/failure + * of the transaction broadcast operation. + */ + public GetKeyReferences(Address address, WitnessResponseListener listener){ + this(address, true, listener); + } + + /** + * Using this constructor the websocket connection closes after the response. + * + * @param addresses + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the success/failure + * of the transaction broadcast operation. + */ + public GetKeyReferences(List
addresses, WitnessResponseListener listener) { + this(addresses, true, listener); } @Override @@ -64,7 +116,9 @@ public class GetKeyReferences extends BaseGrapheneHandler { } else { this.mListener.onSuccess(witnessResponse); } - websocket.disconnect(); + if(mOneTime){ + websocket.disconnect(); + } } @Override diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetLimitOrders.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetLimitOrders.java index eacdda9..3327ff7 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetLimitOrders.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetLimitOrders.java @@ -22,7 +22,14 @@ import de.bitsharesmunich.graphenej.models.BaseResponse; import de.bitsharesmunich.graphenej.models.WitnessResponse; /** - * Created by nelson on 11/15/16. + * Class that implements get_limit_orders request handler. + * + * Get limit orders in a given market. + * + * The request returns the limit orders, ordered from least price to greatest + * + * @see get_limit_orders API doc + * */ public class GetLimitOrders extends BaseGrapheneHandler { @@ -31,12 +38,42 @@ public class GetLimitOrders extends BaseGrapheneHandler { private int limit; private WitnessResponseListener mListener; - public GetLimitOrders(String a, String b, int limit, WitnessResponseListener mListener) { - super(mListener); + private boolean mOneTime; + + + /** + * Default Constructor + * + * @param a id of asset being sold + * @param b id of asset being purchased + * @param limit maximum number of orders to retrieve + * @param oneTime boolean value indicating if WebSocket must be closed (true) or not + * (false) after the response + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. + */ + public GetLimitOrders(String a, String b, int limit, boolean oneTime, WitnessResponseListener listener) { + super(listener); this.a = a; this.b = b; this.limit = limit; - this.mListener = mListener; + this.mOneTime = oneTime; + this.mListener = listener; + } + + /** + * Using this constructor the WebSocket connection closes after the response. + * + * @param a id of asset being sold + * @param b id of asset being purchased + * @param limit maximum number of orders to retrieve + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. + */ + public GetLimitOrders(String a, String b, int limit, WitnessResponseListener listener) { + this(a, b, limit, true, listener); } @Override @@ -70,7 +107,9 @@ public class GetLimitOrders extends BaseGrapheneHandler { } catch (Exception e) { e.printStackTrace(); } - websocket.disconnect(); + if(mOneTime){ + websocket.disconnect(); + } } @Override @@ -83,12 +122,16 @@ public class GetLimitOrders extends BaseGrapheneHandler { @Override public void onError(WebSocket websocket, WebSocketException cause) throws Exception { mListener.onError(new BaseResponse.Error(cause.getMessage())); - websocket.disconnect(); + if(mOneTime){ + websocket.disconnect(); + } } @Override public void handleCallbackError(WebSocket websocket, Throwable cause) throws Exception { mListener.onError(new BaseResponse.Error(cause.getMessage())); - websocket.disconnect(); + if(mOneTime){ + websocket.disconnect(); + } } } diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetMarketHistory.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetMarketHistory.java index b570839..06e2a7f 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetMarketHistory.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetMarketHistory.java @@ -23,7 +23,12 @@ import de.bitsharesmunich.graphenej.models.BucketObject; import de.bitsharesmunich.graphenej.models.WitnessResponse; /** - * Created by nelson on 12/22/16. + * Class that implements get_market_history request handler. + * + * Get mar + * + * @see get_market_history API doc + * */ public class GetMarketHistory extends BaseGrapheneHandler { @@ -44,16 +49,59 @@ public class GetMarketHistory extends BaseGrapheneHandler { private int apiId = -1; private int counter = 0; - public GetMarketHistory(Asset base, Asset quote, long bucket, Date start, Date end, WitnessResponseListener listener){ + private boolean mOneTime; + + /** + * Default Constructor + * + * @param base asset which history is desired + * @param quote asset which the base price asset will be compared to + * @param bucket the time interval (in seconds) for each point should be (analog to + * candles on a candle stick graph). + * Note: The bucket value is discrete and node dependent. The default value + * is 3600s. To get the available buckets of a node use + * get_all_asset_holders API call. + * @param start datetime of of the most recent operation to retrieve (Note: The name is + * counter intuitive, but it follow the original API parameter name) + * @param end datetime of the the earliest operation to retrieve + * @param oneTime boolean value indicating if WebSocket must be closed (true) or not + * (false) after the response + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. + */ + public GetMarketHistory(Asset base, Asset quote, long bucket, Date start, Date end, boolean oneTime, WitnessResponseListener listener){ super(listener); this.base = base; this.quote = quote; this.bucket = bucket; this.start = start; this.end = end; + this.mOneTime = oneTime; this.mListener = listener; } + /** + * Using this constructor the WebSocket connection closes after the response. + * + * @param base asset which history is desired + * @param quote asset which the base price asset will be compared to + * @param bucket the time interval (in seconds) for each point should be (analog to + * candles on a candle stick graph). + * Note: The bucket value is discrete and node dependent. The default value + * is 3600s. To get the available buckets of a node use + * get_all_asset_holders API call. + * @param start datetime of of the most recent operation to retrieve (Note: The name is + * counter intuitive, but it follow the original API parameter name) + * @param end datetime of the the earliest operation to retrieve + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. + */ + public GetMarketHistory(Asset base, Asset quote, long bucket, Date start, Date end, WitnessResponseListener listener){ + this(base, quote, bucket, start, end, true, listener); + } + public Asset getBase() { return base; } @@ -131,7 +179,9 @@ public class GetMarketHistory extends BaseGrapheneHandler { BaseResponse baseResponse = gson.fromJson(response, BaseResponse.class); if(baseResponse.error != null){ mListener.onError(baseResponse.error); - websocket.disconnect(); + if(mOneTime){ + websocket.disconnect(); + } }else{ currentId++; ArrayList emptyParams = new ArrayList<>(); diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetObjects.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetObjects.java index 548dfcd..9c34966 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetObjects.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetObjects.java @@ -28,14 +28,47 @@ import de.bitsharesmunich.graphenej.models.BitAssetData; import de.bitsharesmunich.graphenej.models.WitnessResponse; /** - * Created by nelson on 1/8/17. + * + * Class that implements get_objects request handler. + * + * Get the objects corresponding to the provided IDs. + * + * The response returns a list of objects retrieved, in the order they are mentioned in ids + * + * @see get_objects API doc + * */ public class GetObjects extends BaseGrapheneHandler { private List ids; - public GetObjects(List ids, WitnessResponseListener listener){ + private boolean mOneTime; + + /** + * Default Constructor + * + * @param ids list of IDs of the objects to retrieve + * @param oneTime boolean value indicating if WebSocket must be closed (true) or not + * (false) after the response + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. + */ + public GetObjects(List ids, boolean oneTime, WitnessResponseListener listener){ super(listener); this.ids = ids; + this.mOneTime = oneTime; + } + + /** + * Using this constructor the WebSocket connection closes after the response. + * + * @param ids list of IDs of the objects to retrieve + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. + */ + public GetObjects(List ids, WitnessResponseListener listener){ + this(ids, true, listener); } @Override @@ -92,7 +125,9 @@ public class GetObjects extends BaseGrapheneHandler { WitnessResponse> output = new WitnessResponse<>(); output.result = parsedResult; mListener.onSuccess(output); - websocket.disconnect(); + if(mOneTime){ + websocket.disconnect(); + } } @Override diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetRelativeAccountHistory.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetRelativeAccountHistory.java index fda7b19..32da128 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetRelativeAccountHistory.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetRelativeAccountHistory.java @@ -20,6 +20,7 @@ import de.bitsharesmunich.graphenej.models.ApiCall; import de.bitsharesmunich.graphenej.models.BaseResponse; import de.bitsharesmunich.graphenej.models.HistoricalTransfer; import de.bitsharesmunich.graphenej.models.WitnessResponse; +import de.bitsharesmunich.graphenej.objects.Memo; import de.bitsharesmunich.graphenej.operations.TransferOperation; /** @@ -48,38 +49,81 @@ public class GetRelativeAccountHistory extends BaseGrapheneHandler { private int currentId = 1; private int apiId = -1; + private boolean mOneTime; + /** * Constructor that takes all possible parameters. - * @param userAccount The user account to be queried - * @param stop Sequence number of earliest operation - * @param limit Maximum number of operations to retrieve (must not exceed 100) - * @param start Sequence number of the most recent operation to retrieve - * @param listener Listener to be notified with the result of this query + * + * @param userAccount The user account to be queried + * @param stop Sequence number of earliest operation + * @param limit Maximum number of operations to retrieve (must not exceed 100) + * @param start Sequence number of the most recent operation to retrieve + * @param oneTime boolean value indicating if WebSocket must be closed (true) or not + * (false) after the response + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. */ - public GetRelativeAccountHistory(UserAccount userAccount, int stop, int limit, int start, WitnessResponseListener listener){ + public GetRelativeAccountHistory(UserAccount userAccount, int stop, int limit, int start, boolean oneTime, WitnessResponseListener listener){ super(listener); if(limit > MAX_LIMIT) limit = MAX_LIMIT; this.mUserAccount = userAccount; this.stop = stop; this.limit = limit; this.start = start; + this.mOneTime = oneTime; this.mListener = listener; } /** * Constructor that uses the default values, and sets the limit to its maximum possible value. - * @param userAccount The user account to be queried - * @param listener Listener to be notified with the result of this query + * + * @param userAccount The user account to be queried + * @param oneTime boolean value indicating if WebSocket must be closed (true) or not + * (false) after the response + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. */ - public GetRelativeAccountHistory(UserAccount userAccount, WitnessResponseListener listener){ + public GetRelativeAccountHistory(UserAccount userAccount, boolean oneTime, WitnessResponseListener listener){ super(listener); this.mUserAccount = userAccount; this.stop = DEFAULT_STOP; this.limit = MAX_LIMIT; this.start = DEFAULT_START; + this.mOneTime = oneTime; this.mListener = listener; } + /** + * Constructor that takes all possible parameters for the query. + * Using this constructor the WebSocket connection closes after the response. + * + * @param userAccount The user account to be queried + * @param stop Sequence number of earliest operation + * @param limit Maximum number of operations to retrieve (must not exceed 100) + * @param start Sequence number of the most recent operation to retrieve + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. + */ + public GetRelativeAccountHistory(UserAccount userAccount, int stop, int limit, int start, WitnessResponseListener listener){ + this(userAccount, stop, limit, start, true, listener); + } + + /** + * Constructor that uses the default values, and sets the limit to its maximum possible value. + * Using this constructor the WebSocket connection closes after the response. + * + * @param userAccount The user account to be queried + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. + */ + public GetRelativeAccountHistory(UserAccount userAccount, WitnessResponseListener listener){ + this(userAccount, true, listener); + } + @Override public void onConnected(WebSocket websocket, Map> headers) throws Exception { mWebsocket = websocket; @@ -98,7 +142,9 @@ public class GetRelativeAccountHistory extends BaseGrapheneHandler { BaseResponse baseResponse = gson.fromJson(response, BaseResponse.class); if(baseResponse.error != null){ mListener.onError(baseResponse.error); - websocket.disconnect(); + if(mOneTime){ + websocket.disconnect(); + } }else{ currentId++; ArrayList emptyParams = new ArrayList<>(); @@ -116,6 +162,7 @@ public class GetRelativeAccountHistory extends BaseGrapheneHandler { GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.registerTypeAdapter(TransferOperation.class, new TransferOperation.TransferDeserializer()); gsonBuilder.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer()); + gsonBuilder.registerTypeAdapter(Memo.class, new Memo.MemoDeserializer()); WitnessResponse> transfersResponse = gsonBuilder.create().fromJson(response, RelativeAccountHistoryResponse); mListener.onSuccess(transfersResponse); } @@ -127,7 +174,7 @@ public class GetRelativeAccountHistory extends BaseGrapheneHandler { */ private void sendRelativeAccountHistoryRequest(){ ArrayList params = new ArrayList<>(); - params.add(mUserAccount.toJsonString()); + params.add(mUserAccount.getObjectId()); params.add(this.stop); params.add(this.limit); params.add(this.start); @@ -137,7 +184,8 @@ public class GetRelativeAccountHistory extends BaseGrapheneHandler { } /** - * Updates the arguments and makes a new call to the get_relative_account_history API + * Updates the arguments and makes a new call to the get_relative_account_history API. + * * @param stop Sequence number of earliest operation * @param limit Maximum number of operations to retrieve (must not exceed 100) * @param start Sequence number of the most recent operation to retrieve @@ -150,10 +198,10 @@ public class GetRelativeAccountHistory extends BaseGrapheneHandler { } /** - * Disconnects the websocket + * Disconnects the WebSocket. */ public void disconnect(){ - if(mWebsocket != null && mWebsocket.isOpen()){ + if(mWebsocket != null && mWebsocket.isOpen() && mOneTime){ mWebsocket.disconnect(); } } diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetRequiredFees.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetRequiredFees.java index f82b950..553c95d 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetRequiredFees.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetRequiredFees.java @@ -23,20 +23,52 @@ import java.util.List; import java.util.Map; /** - * Created by nelson on 11/15/16. + * Class that implements get_required_fees request handler. + * + * For each operation calculate the required fee in the specified asset type. + * + * @see get_required_fees API doc */ -public class GetRequiredFees extends WebSocketAdapter { +public class GetRequiredFees extends BaseGrapheneHandler { private WitnessResponseListener mListener; private List operations; private Asset asset; - public GetRequiredFees(List operations, Asset asset, WitnessResponseListener listener){ + private boolean mOneTime; + + /** + * Default Constructor + * + * @param operations list of operations that fee should be calculated + * @param asset specify the asset of the operations + * @param oneTime boolean value indicating if WebSocket must be closed (true) or not + * (false) after the response + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. + */ + public GetRequiredFees(List operations, Asset asset, boolean oneTime, WitnessResponseListener listener){ + super(listener); this.operations = operations; this.asset = asset; + this.mOneTime = oneTime; this.mListener = listener; } + /** + * Using this constructor the WebSocket connection closes after the response. + * + * @param operations list of operations that fee should be calculated + * @param asset specify the asset of the operations + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. + */ + public GetRequiredFees(List operations, Asset asset, WitnessResponseListener listener){ + this(operations, asset, true, listener); + } + @Override public void onConnected(WebSocket websocket, Map> headers) throws Exception { ArrayList accountParams = new ArrayList<>(); @@ -66,12 +98,16 @@ public class GetRequiredFees extends WebSocketAdapter { @Override public void onError(WebSocket websocket, WebSocketException cause) throws Exception { mListener.onError(new BaseResponse.Error(cause.getMessage())); - websocket.disconnect(); + if(mOneTime){ + websocket.disconnect(); + } } @Override public void handleCallbackError(WebSocket websocket, Throwable cause) throws Exception { mListener.onError(new BaseResponse.Error(cause.getMessage())); - websocket.disconnect(); + if(mOneTime){ + websocket.disconnect(); + } } } diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetTradeHistory.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetTradeHistory.java index 46dd72d..dde5665 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetTradeHistory.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetTradeHistory.java @@ -18,7 +18,15 @@ import de.bitsharesmunich.graphenej.models.ApiCall; import de.bitsharesmunich.graphenej.models.WitnessResponse; /** - * @author henry + * Class that implements get_trade_history request handler. + * + * Get recent trades for the market assetA:assetB for a time interval + * Note: Currently, timezone offsets are not supported. The time must be UTC. + * + * The request returns the all trades of the passed pair of asset at a specific time interval. + * + * @see get_trade_history API doc + * */ public class GetTradeHistory extends BaseGrapheneHandler { @@ -29,14 +37,47 @@ public class GetTradeHistory extends BaseGrapheneHandler { private int limit; private WitnessResponseListener mListener; - public GetTradeHistory(String a, String b, String toTime, String fromTime,int limit, WitnessResponseListener mListener) { - super(mListener); + private boolean mOneTime; + + /** + * Constructor + * + * @param a name of the first asset + * @param b name of the second asset + * @param toTime stop time as a UNIX timestamp + * @param fromTime start time as a UNIX timestamp + * @param limit number of transactions to retrieve, capped at 100 + * @param oneTime boolean value indicating if WebSocket must be closed (true) or not + * (false) after the response + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. + */ + public GetTradeHistory(String a, String b, String toTime, String fromTime,int limit, boolean oneTime, WitnessResponseListener listener) { + super(listener); this.a = a; this.b = b; this.toTime = toTime; this.fromTime = fromTime; this.limit = limit; - this.mListener = mListener; + this.mOneTime = oneTime; + this.mListener = listener; + } + + /** + * Using this constructor the WebSocket connection closes after the response. + * + * @param a name of the first asset + * @param b name of the second asset + * @param toTime stop time as a UNIX timestamp + * @param fromTime start time as a UNIX timestamp + * @param limit number of transactions to retrieve, capped at 100 + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. + */ + public GetTradeHistory(String a, String b, String toTime, String fromTime,int limit, WitnessResponseListener listener) { + this(a, b, toTime, fromTime, limit, true, listener); } @Override @@ -72,7 +113,9 @@ public class GetTradeHistory extends BaseGrapheneHandler { } catch (Exception e) { e.printStackTrace(); } - websocket.disconnect(); + if(mOneTime){ + websocket.disconnect(); + } } @Override diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/ListAssets.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/ListAssets.java index 5a674cc..28fb6bb 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/ListAssets.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/ListAssets.java @@ -19,14 +19,12 @@ import java.util.Map; /** * WebSocketAdapter class used to send a request a 'list_assets' API call to the witness node. * - * @see: - * * The API imposes a limit of of 100 assets per request, but if the user of this class wants * to get a list of all assets, the LIST_ALL constant must be used as second argument in the * constructor. Internally we are going to perform multiple calls in order to satisfy the user's * request. * - * Created by nelson on 1/5/17. + * @see: */ public class ListAssets extends BaseGrapheneHandler { /** @@ -45,16 +43,41 @@ public class ListAssets extends BaseGrapheneHandler { private int limit; private int requestCounter = 0; + private boolean mOneTime; + /** * Constructor - * @param lowerBoundSymbol: Lower bound of symbol names to retrieve - * @param limit: Maximum number of assets to fetch, if the constant LIST_ALL - * is passed, all existing assets will be retrieved. + * + * @param lowerBoundSymbol Lower bound of symbol names to retrieve + * @param limit Maximum number of assets to fetch, if the constant LIST_ALL + * is passed, all existing assets will be retrieved. + * @param oneTime boolean value indicating if WebSocket must be closed (true) or not + * (false) after the response + * @param listener A class implementing the WitnessResponseListener interface. This + * should be implemented by the party interested in being notified + * about the success/failure of the operation. */ - public ListAssets(String lowerBoundSymbol, int limit, WitnessResponseListener listener){ + public ListAssets(String lowerBoundSymbol, int limit, boolean oneTime, WitnessResponseListener listener){ super(listener); this.lowerBound = lowerBoundSymbol; this.limit = limit; + this.mOneTime = oneTime; + } + + /** + * Using this constructor the WebSocket connection closes after the response. + * + * @param lowerBoundSymbol Lower bound of symbol names to retrieve + * @param limit Maximum number of assets to fetch, if the constant LIST_ALL + * is passed, all existing assets will be retrieved. + * @param oneTime boolean value indicating if WebSocket must be closed (true) or not + * (false) after the response + * @param listener A class implementing the WitnessResponseListener interface. This + * should be implemented by the party interested in being notified + * about the success/failure of the operation. + */ + public ListAssets(String lowerBoundSymbol, int limit, WitnessResponseListener listener){ + this(lowerBoundSymbol, limit, true, listener); } @Override @@ -81,7 +104,9 @@ public class ListAssets extends BaseGrapheneHandler { // If the requested number of assets was below // the limit, we just call the listener. mListener.onSuccess(witnessResponse); - websocket.disconnect(); + if(mOneTime){ + websocket.disconnect(); + } }else{ // Updating counter to keep track of how many batches we already retrieved. requestCounter++; @@ -96,12 +121,16 @@ public class ListAssets extends BaseGrapheneHandler { // we got less than the requested amount. witnessResponse.result = this.assets; mListener.onSuccess(witnessResponse); - websocket.disconnect(); + if(mOneTime){ + websocket.disconnect(); + } }else if(this.assets.size() == this.limit){ // We already have the required amount of assets witnessResponse.result = this.assets; mListener.onSuccess(witnessResponse); - websocket.disconnect(); + if(mOneTime){ + websocket.disconnect(); + } }else{ // We still need to fetch some more assets this.lowerBound = this.assets.get(this.assets.size() - 1).getSymbol(); diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/LookupAccounts.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/LookupAccounts.java index 6b424b9..97ab2bf 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/LookupAccounts.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/LookupAccounts.java @@ -18,7 +18,13 @@ import de.bitsharesmunich.graphenej.models.ApiCall; import de.bitsharesmunich.graphenej.models.WitnessResponse; /** - * Created by henry on 07/12/16. + * Class that implements lookup_accounts request handler. + * + * Get names and IDs for registered accounts. + * + * The request returns a map of account names to corresponding IDs. + * + * @see lookup_accounts API doc */ public class LookupAccounts extends BaseGrapheneHandler { @@ -27,20 +33,70 @@ public class LookupAccounts extends BaseGrapheneHandler { private int maxAccounts = DEFAULT_MAX; private final WitnessResponseListener mListener; - public LookupAccounts(String accountName, WitnessResponseListener listener){ + private boolean mOneTime; + + /** + * Constructor + * + * @param accountName account name used at the query + * @param oneTime boolean value indicating if WebSocket must be closed (true) or not + * (false) after the response + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. + */ + public LookupAccounts(String accountName, boolean oneTime, WitnessResponseListener listener){ super(listener); this.accountName = accountName; this.maxAccounts = DEFAULT_MAX; + this.mOneTime = oneTime; this.mListener = listener; } - public LookupAccounts(String accountName, int maxAccounts, WitnessResponseListener listener){ + /** + * Constructor with maxAccounts + * + * @param accountName account name used at the query + * @param maxAccounts maximum number of results to return (must not exceed 1000) + * @param oneTime boolean value indicating if WebSocket must be closed (true) or not + * (false) after the response + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. + */ + public LookupAccounts(String accountName, int maxAccounts, boolean oneTime, WitnessResponseListener listener){ super(listener); this.accountName = accountName; this.maxAccounts = maxAccounts; + this.mOneTime = oneTime; this.mListener = listener; } + /** + * Using this constructor the WebSocket connection closes after the response. + * + * @param accountName account name used at the query + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. + */ + public LookupAccounts(String accountName, WitnessResponseListener listener){ + this(accountName, true, listener); + } + + /** + * Using this constructor the WebSocket connection closes after the response. + * + * @param accountName account name used at the query + * @param maxAccounts maximum number of results to return (must not exceed 1000) + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. + */ + public LookupAccounts(String accountName, int maxAccounts, WitnessResponseListener listener){ + this(accountName, maxAccounts, true, listener); + } + @Override public void onConnected(WebSocket websocket, Map> headers) throws Exception { ArrayList accountParams = new ArrayList<>(); @@ -65,7 +121,9 @@ public class LookupAccounts extends BaseGrapheneHandler { this.mListener.onSuccess(witnessResponse); } - websocket.disconnect(); + if(mOneTime){ + websocket.disconnect(); + } } @Override diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/LookupAssetSymbols.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/LookupAssetSymbols.java index 3c24247..2a649d0 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/LookupAssetSymbols.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/LookupAssetSymbols.java @@ -18,18 +18,49 @@ import de.bitsharesmunich.graphenej.models.ApiCall; import de.bitsharesmunich.graphenej.models.WitnessResponse; /** - * Created by nelson on 12/12/16. + * Class that implements lookup_asset_symbols request handler. + * + * Get the assets corresponding to the provided IDs. + * + * The response returns the assets corresponding to the provided symbols or IDs. + * + * @see lookup_asset_symbols API doc */ public class LookupAssetSymbols extends BaseGrapheneHandler { private WitnessResponseListener mListener; private List assets; - public LookupAssetSymbols(List assets, WitnessResponseListener listener){ + private boolean mOneTime; + + /** + * Default Constructor + * + * @param assets list of the assets to retrieve + * @param oneTime boolean value indicating if WebSocket must be closed (true) or not + * (false) after the response + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. + */ + public LookupAssetSymbols(List assets, boolean oneTime, WitnessResponseListener listener){ super(listener); this.assets = assets; + this.mOneTime = oneTime; this.mListener = listener; } + /** + * Using this constructor the WebSocket connection closes after the response. + * + * @param assets list of the assets to retrieve + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. + */ + public LookupAssetSymbols(List assets, WitnessResponseListener listener){ + this(assets, true, listener); + } + @Override public void onConnected(WebSocket websocket, Map> headers) throws Exception { ArrayList params = new ArrayList<>(); @@ -51,6 +82,9 @@ public class LookupAssetSymbols extends BaseGrapheneHandler { gsonBuilder.registerTypeAdapter(Asset.class, new Asset.AssetDeserializer()); WitnessResponse> witnessResponse = gsonBuilder.create().fromJson(response, LookupAssetSymbolsResponse); mListener.onSuccess(witnessResponse); + if(mOneTime){ + websocket.disconnect(); + } } @Override diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/SubscriptionMessagesHub.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/SubscriptionMessagesHub.java index 510b8c8..2be0780 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/SubscriptionMessagesHub.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/SubscriptionMessagesHub.java @@ -9,14 +9,16 @@ import com.neovisionaries.ws.client.WebSocketFrame; import java.io.Serializable; import java.lang.reflect.Type; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import de.bitsharesmunich.graphenej.AssetAmount; -import de.bitsharesmunich.graphenej.ObjectType; import de.bitsharesmunich.graphenej.RPC; import de.bitsharesmunich.graphenej.Transaction; import de.bitsharesmunich.graphenej.UserAccount; +import de.bitsharesmunich.graphenej.errors.RepeatedRequestIdException; +import de.bitsharesmunich.graphenej.interfaces.NodeErrorListener; import de.bitsharesmunich.graphenej.interfaces.SubscriptionHub; import de.bitsharesmunich.graphenej.interfaces.SubscriptionListener; import de.bitsharesmunich.graphenej.interfaces.WitnessResponseListener; @@ -24,37 +26,44 @@ import de.bitsharesmunich.graphenej.models.ApiCall; import de.bitsharesmunich.graphenej.models.DynamicGlobalProperties; import de.bitsharesmunich.graphenej.models.SubscriptionResponse; import de.bitsharesmunich.graphenej.models.WitnessResponse; +import de.bitsharesmunich.graphenej.objects.Memo; import de.bitsharesmunich.graphenej.operations.LimitOrderCreateOperation; import de.bitsharesmunich.graphenej.operations.TransferOperation; /** - * A websocket adapter prepared to be used as a basic dispatch hub for subscription messages. - * - * Created by nelson on 1/26/17. + * A WebSocket adapter prepared to be used as a basic dispatch hub for subscription messages. */ public class SubscriptionMessagesHub extends BaseGrapheneHandler implements SubscriptionHub { + + private WebSocket mWebsocket; + // Sequence of message ids - private final static int LOGIN_ID = 1; - private final static int GET_DATABASE_ID = 2; - private final static int SUBCRIPTION_REQUEST = 3; + public final static int LOGIN_ID = 1; + public final static int GET_DATABASE_ID = 2; + public final static int SUBSCRIPTION_REQUEST = 3; // ID of subscription notifications - private final static int SUBCRIPTION_NOTIFICATION = 4; + public final static int SUBSCRIPTION_NOTIFICATION = 4; + + /** + * Id attributed to the indivitual 'get_objects' API call required for a fine-grained + * subscription request. + */ + public final static int MANUAL_SUBSCRIPTION_ID = 5; private SubscriptionResponse.SubscriptionResponseDeserializer mSubscriptionDeserializer; private Gson gson; private String user; private String password; private boolean clearFilter; - private List objectTypes; private int currentId; private int databaseApiId = -1; private int subscriptionCounter = 0; + private HashMap mHandlerMap = new HashMap<>(); - /** - * Id used to separate requests regarding the subscriptions - */ - private final int SUBSCRIPTION_ID = 10; + // State variables + private boolean isUnsubscribing; + private boolean isSubscribed; /** * Constructor used to create a subscription message hub that will call the set_subscribe_callback @@ -63,17 +72,17 @@ public class SubscriptionMessagesHub extends BaseGrapheneHandler implements Subs * * A list of ObjectTypes must be provided, otherwise we won't get any update. * - * @param user: User name, in case the node to which we're going to connect to requires authentication - * @param password: Password, same as above - * @param objectTypes: List of objects of interest - * @param errorListener: Callback that will be fired in case there is an error. + * @param user User name, in case the node to which we're going to connect to requires + * authentication + * @param password Password, same as above + * @param clearFilter Whether to automatically subscribe of not to the notification feed. + * @param errorListener Callback that will be fired in case there is an error. */ - public SubscriptionMessagesHub(String user, String password, List objectTypes, WitnessResponseListener errorListener){ + public SubscriptionMessagesHub(String user, String password, boolean clearFilter, NodeErrorListener errorListener){ super(errorListener); - this.objectTypes = objectTypes; this.user = user; this.password = password; - this.clearFilter = true; + this.clearFilter = clearFilter; this.mSubscriptionDeserializer = new SubscriptionResponse.SubscriptionResponseDeserializer(); GsonBuilder builder = new GsonBuilder(); builder.registerTypeAdapter(SubscriptionResponse.class, mSubscriptionDeserializer); @@ -83,20 +92,22 @@ public class SubscriptionMessagesHub extends BaseGrapheneHandler implements Subs builder.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer()); builder.registerTypeAdapter(UserAccount.class, new UserAccount.UserAccountSimpleDeserializer()); builder.registerTypeAdapter(DynamicGlobalProperties.class, new DynamicGlobalProperties.DynamicGlobalPropertiesDeserializer()); + builder.registerTypeAdapter(Memo.class, new Memo.MemoDeserializer()); this.gson = builder.create(); } /** - * Constructor used to create a subscription message hub that will call the set_subscribe_callback - * API with the clear_filter parameter set to true, meaning that it will receive automatic updates - * on all network events. + * Constructor used to create a subscription message hub that will call the + * set_subscribe_callback API with the clear_filter parameter set to false, meaning that it will + * only receive updates from objects we register. * - * @param user: User name, in case the node to which we're going to connect to requires authentication - * @param password: Password, same as above - * @param errorListener: Callback that will be fired in case there is an error. + * @param user User name, in case the node to which we're going to connect to requires + * authentication + * @param password Password, same as above + * @param errorListener Callback that will be fired in case there is an error. */ - public SubscriptionMessagesHub(String user, String password, WitnessResponseListener errorListener){ - this(user, password, new ArrayList(), errorListener); + public SubscriptionMessagesHub(String user, String password, NodeErrorListener errorListener){ + this(user, password, false, errorListener); } @Override @@ -116,6 +127,7 @@ public class SubscriptionMessagesHub extends BaseGrapheneHandler implements Subs @Override public void onConnected(WebSocket websocket, Map> headers) throws Exception { + this.mWebsocket = websocket; ArrayList loginParams = new ArrayList<>(); currentId = LOGIN_ID; loginParams.add(user); @@ -138,23 +150,46 @@ public class SubscriptionMessagesHub extends BaseGrapheneHandler implements Subs WitnessResponse witnessResponse = gson.fromJson(message, ApiIdResponse); databaseApiId = witnessResponse.result; - ArrayList subscriptionParams = new ArrayList<>(); - subscriptionParams.add(String.format("%d", SUBCRIPTION_NOTIFICATION)); - subscriptionParams.add(clearFilter); - ApiCall getDatabaseId = new ApiCall(databaseApiId, RPC.CALL_SET_SUBSCRIBE_CALLBACK, subscriptionParams, RPC.VERSION, SUBCRIPTION_REQUEST); - websocket.sendText(getDatabaseId.toJsonString()); - currentId++; - } else if(currentId == SUBCRIPTION_REQUEST){ - if(objectTypes != null && objectTypes.size() > 0 && subscriptionCounter < objectTypes.size()){ - ArrayList objectOfInterest = new ArrayList<>(); - objectOfInterest.add(objectTypes.get(subscriptionCounter).getGenericObjectId()); + subscribe(); + } else if(currentId == SUBSCRIPTION_REQUEST){ + List subscriptionListeners = mSubscriptionDeserializer.getSubscriptionListeners(); + + if(!isUnsubscribing){ + isSubscribed = true; + } + + // If we haven't subscribed to all requested subscription channels yet, + // just send one more subscription + if(subscriptionListeners != null && + subscriptionListeners.size() > 0 && + subscriptionCounter < subscriptionListeners.size()){ + + ArrayList objects = new ArrayList<>(); ArrayList payload = new ArrayList<>(); - payload.add(objectOfInterest); - ApiCall subscribe = new ApiCall(databaseApiId, RPC.GET_OBJECTS, payload, RPC.VERSION, SUBSCRIPTION_ID); + for(SubscriptionListener listener : subscriptionListeners){ + objects.add(listener.getInterestObjectType().getGenericObjectId()); + } + + payload.add(objects); + ApiCall subscribe = new ApiCall(databaseApiId, RPC.GET_OBJECTS, payload, RPC.VERSION, MANUAL_SUBSCRIPTION_ID); websocket.sendText(subscribe.toJsonString()); subscriptionCounter++; }else{ - gson.fromJson(message, SubscriptionResponse.class); + WitnessResponse witnessResponse = gson.fromJson(message, WitnessResponse.class); + if(witnessResponse.result != null && + mHandlerMap.get(witnessResponse.id) != null){ + // This is the response to a request that was submitted to the message hub + // and whose handler was stored in the "request id" -> "handler" map + BaseGrapheneHandler handler = mHandlerMap.get(witnessResponse.id); + handler.onTextFrame(websocket, frame); + mHandlerMap.remove(witnessResponse.id); + }else{ + // If we've already subscribed to all requested subscription channels, we + // just proceed to deserialize content. + // The deserialization is handled by all those TypeAdapters registered in the class + // constructor while building the gson instance. + SubscriptionResponse response = gson.fromJson(message, SubscriptionResponse.class); + } } } } @@ -164,9 +199,86 @@ public class SubscriptionMessagesHub extends BaseGrapheneHandler implements Subs System.out.println(">> "+frame.getPayloadText()); } + /** + * Private method that sends a subscription request to the full node + */ + private void subscribe(){ + isUnsubscribing = false; + + ArrayList subscriptionParams = new ArrayList<>(); + subscriptionParams.add(String.format("%d", SUBSCRIPTION_NOTIFICATION)); + subscriptionParams.add(clearFilter); + ApiCall getDatabaseId = new ApiCall(databaseApiId, RPC.CALL_SET_SUBSCRIBE_CALLBACK, subscriptionParams, RPC.VERSION, SUBSCRIPTION_REQUEST); + mWebsocket.sendText(getDatabaseId.toJsonString()); + currentId = SUBSCRIPTION_REQUEST; + } + + /** + * Public method used to re-establish a subscription after it was cancelled by a previous + * call to the {@see #cancelSubscriptions()} method call. + * + * Please note that you should repeat the registration step for every interested listener, since + * those were probably lost after the previous {@see #cancelSubscriptions()} method call. + */ + public void resubscribe(){ + if(mWebsocket.isOpen()){ + subscribe(); + }else{ + throw new IllegalStateException("Websocket is not open, can't resubscribe"); + } + } + + /** + * Method that sends a subscription cancellation request to the full node, and also + * de-registers all subscription and request listeners. + */ + public void cancelSubscriptions(){ + isSubscribed = false; + isUnsubscribing = true; + + ApiCall unsubscribe = new ApiCall(databaseApiId, RPC.CALL_CANCEL_ALL_SUBSCRIPTIONS, new ArrayList(), RPC.VERSION, SUBSCRIPTION_REQUEST); + mWebsocket.sendText(unsubscribe.toJsonString()); + + // Clearing all subscription listeners + mSubscriptionDeserializer.clearAllSubscriptionListeners(); + + // Clearing all request handler listners + mHandlerMap.clear(); + } + + /** + * Method used to check the current state of the connection. + * + * @return True if the websocket is open and there is an active subscription, false otherwise. + */ + public boolean isSubscribed(){ + return this.mWebsocket.isOpen() && isSubscribed; + } + + /** + * Method used to reset all internal variables. + */ public void reset(){ currentId = 0; databaseApiId = -1; subscriptionCounter = 0; } + + public void addRequestHandler(BaseGrapheneHandler handler) throws RepeatedRequestIdException { + if(mHandlerMap.get(handler.getRequestId()) != null){ + throw new RepeatedRequestIdException("Already registered handler with id: "+handler.getRequestId()); + } + + mHandlerMap.put(handler.getRequestId(), handler); + + try { + // Artificially calling the 'onConnected' method of the handler. + // The underlying websocket was already connected, but from the WebSocketAdapter + // point of view it doesn't make a difference. + handler.onConnected(mWebsocket, null); + } catch (Exception e) { + System.out.println("Exception. Msg: "+e.getMessage()); + System.out.println("Exception type: "+e); + } + } } diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/TransactionBroadcastSequence.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/TransactionBroadcastSequence.java index 30549d9..6064df5 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/TransactionBroadcastSequence.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/TransactionBroadcastSequence.java @@ -43,20 +43,38 @@ public class TransactionBroadcastSequence extends BaseGrapheneHandler { private int currentId = 1; private int broadcastApiId = -1; + private boolean mOneTime; + /** - * Constructor of this class. The ids required - * @param transaction: The transaction to be broadcasted. - * @param listener: A class implementing the WitnessResponseListener interface. This should - * be implemented by the party interested in being notified about the success/failure - * of the transaction broadcast operation. + * Default Constructor + * + * @param transaction transaction to be broadcasted. + * @param oneTime boolean value indicating if WebSocket must be closed (true) or not + * (false) after the response + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. */ - public TransactionBroadcastSequence(Transaction transaction, Asset feeAsset, WitnessResponseListener listener){ + public TransactionBroadcastSequence(Transaction transaction, Asset feeAsset, boolean oneTime, WitnessResponseListener listener){ super(listener); this.transaction = transaction; this.feeAsset = feeAsset; + this.mOneTime = oneTime; this.mListener = listener; } + /** + * Using this constructor the WebSocket connection closes after the response. + * + * @param transaction: transaction to be broadcasted. + * @param listener A class implementing the WitnessResponseListener interface. This should + * be implemented by the party interested in being notified about the + * success/failure of the operation. + */ + public TransactionBroadcastSequence(Transaction transaction, Asset feeAsset, WitnessResponseListener listener){ + this(transaction, feeAsset, true, listener); + } + @Override public void onConnected(WebSocket websocket, Map> headers) throws Exception { ArrayList loginParams = new ArrayList<>(); @@ -77,7 +95,9 @@ public class TransactionBroadcastSequence extends BaseGrapheneHandler { BaseResponse baseResponse = gson.fromJson(response, BaseResponse.class); if(baseResponse.error != null){ mListener.onError(baseResponse.error); - websocket.disconnect(); + if(mOneTime){ + websocket.disconnect(); + } }else{ currentId++; ArrayList emptyParams = new ArrayList<>(); @@ -140,7 +160,9 @@ public class TransactionBroadcastSequence extends BaseGrapheneHandler { Type WitnessResponseType = new TypeToken>(){}.getType(); WitnessResponse witnessResponse = gson.fromJson(response, WitnessResponseType); mListener.onSuccess(witnessResponse); - websocket.disconnect(); + if(mOneTime){ + websocket.disconnect(); + } } } } @@ -156,7 +178,9 @@ public class TransactionBroadcastSequence extends BaseGrapheneHandler { public void onError(WebSocket websocket, WebSocketException cause) throws Exception { System.out.println("onError. cause: "+cause.getMessage()); mListener.onError(new BaseResponse.Error(cause.getMessage())); - websocket.disconnect(); + if(mOneTime){ + websocket.disconnect(); + } } @Override @@ -166,6 +190,8 @@ public class TransactionBroadcastSequence extends BaseGrapheneHandler { System.out.println(element.getFileName()+"#"+element.getClassName()+":"+element.getLineNumber()); } mListener.onError(new BaseResponse.Error(cause.getMessage())); - websocket.disconnect(); + if(mOneTime){ + websocket.disconnect(); + } } } \ No newline at end of file diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/android/NodeConnection.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/android/NodeConnection.java new file mode 100644 index 0000000..3bd90e8 --- /dev/null +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/android/NodeConnection.java @@ -0,0 +1,143 @@ +package de.bitsharesmunich.graphenej.api.android; + +import java.util.ArrayList; +import java.util.List; + +import de.bitsharesmunich.graphenej.api.BaseGrapheneHandler; +import de.bitsharesmunich.graphenej.api.SubscriptionMessagesHub; +import de.bitsharesmunich.graphenej.errors.RepeatedRequestIdException; +import de.bitsharesmunich.graphenej.interfaces.NodeErrorListener; +import de.bitsharesmunich.graphenej.interfaces.WitnessResponseListener; +import de.bitsharesmunich.graphenej.models.BaseResponse; + +/** + * Class used to encapsulate all connections that should be done to a node (with node hop support). + * + * This class is intended to be used as a central broker for all full node API requests. It should + * be used as a singleton under an application. + */ +public class NodeConnection { + /** + * List of URLs of the nodes + */ + private List mUrlList; + /** + * Index of the current node from the list + */ + private int mUrlIndex; + private WebsocketWorkerThread mThread; + private SubscriptionMessagesHub mMessagesHub; + private long requestCounter = SubscriptionMessagesHub.MANUAL_SUBSCRIPTION_ID + 1; + private WitnessResponseListener mErrorListener; + + private static NodeConnection instance; + + private String mUser; + private String mPassword; + private boolean mSubscribe; + + /* + * Get the instance of the NodeConnection which is intended to be used as a Singleton. + */ + public static NodeConnection getInstance(){ + if(instance == null){ + instance = new NodeConnection(); + } + return instance; + } + + public NodeConnection(){ + this.mUrlList = new ArrayList<>(); + } + + /** + * Add a WebSocket URL node that will be added to the list used at node hop scheme. + * + * @param url: URL of the node + */ + public void addNodeUrl(String url){ + System.out.println("addNodeUrl: "+url); + this.mUrlList.add(url); + } + + /** + * Add a list of WebSocket URL nodes that will be added to the current list and + * be used at node hop scheme. + * + * @param urlList: List of URLs of the nodes + */ + public void addNodeUrls(List urlList){ + List newList = new ArrayList(mUrlList); + newList.addAll(urlList); + } + + /** + * Get the list of WebSocket URL nodes. + * + * @return List of URLs of the nodes + */ + public List getNodeUrls(){ + return this.mUrlList; + } + + /** + * Clear list of WebSocket URL nodes. + */ + public void clearNodeList(){ + this.mUrlList.clear(); + } + + private NodeErrorListener mInternalErrorListener = new NodeErrorListener() { + @Override + public void onError(BaseResponse.Error error) { + System.out.println("NodeConnect Error. Msg: "+error); + + connect(mUser, mPassword, mSubscribe, mErrorListener); + } + }; + + /** + + */ + /** + * Method that will try to connect to one of the nodes. If the connection fails + * a subsequent call to this method will try to connect with the next node in the + * list if there is one. + * + * @param user user credential used for restricted requested that needed to be + * logged + * @param password password credential used for restricted requested that needed to be + * logged + * @param subscribe if the node should be subscribed to the node + * @param errorListener a class implementing the WitnessResponseListener interface. This + * should be implemented by the party interested in being notified + * about the failure of the desired broadcast operation. + */ + public void connect(String user, String password, boolean subscribe, WitnessResponseListener errorListener) { + if(this.mUrlList.size() > 0){ + mUser = user; + mPassword = password; + mSubscribe = subscribe; + System.out.println("Connecting to: "+ this.mUrlList.get(mUrlIndex)); + mErrorListener = errorListener; + mThread = new WebsocketWorkerThread(this.mUrlList.get(mUrlIndex), mInternalErrorListener); + mUrlIndex = mUrlIndex + 1 % this.mUrlList.size(); + + mMessagesHub = new SubscriptionMessagesHub(user, password, subscribe, mInternalErrorListener); + mThread.addListener(mMessagesHub); + mThread.start(); + } + } + + /** + * Add the API Handler to the node. + * + * @param handler request handler to be added to the connection + * @throws RepeatedRequestIdException + */ + public void addRequestHandler(BaseGrapheneHandler handler) throws RepeatedRequestIdException { + handler.setRequestId(requestCounter); + requestCounter++; + mMessagesHub.addRequestHandler(handler); + } +} diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/android/WebsocketWorkerThread.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/android/WebsocketWorkerThread.java new file mode 100644 index 0000000..76db5e0 --- /dev/null +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/android/WebsocketWorkerThread.java @@ -0,0 +1,110 @@ +package de.bitsharesmunich.graphenej.api.android; + +import com.neovisionaries.ws.client.WebSocket; +import com.neovisionaries.ws.client.WebSocketException; +import com.neovisionaries.ws.client.WebSocketFactory; +import com.neovisionaries.ws.client.WebSocketListener; + +import java.io.IOException; +import java.security.NoSuchAlgorithmException; + +import javax.net.ssl.SSLContext; + +import de.bitsharesmunich.graphenej.interfaces.NodeErrorListener; +import de.bitsharesmunich.graphenej.models.BaseResponse; +import de.bitsharesmunich.graphenej.test.NaiveSSLContext; + +/** + * Class used to encapsulate the thread where the WebSocket does the requests. + * + */ +public class WebsocketWorkerThread extends Thread { + private final String TAG = this.getClass().getName(); + + // When debugging we'll use a NaiveSSLContext + public static final boolean DEBUG = true; + + private final int TIMEOUT = 5000; + private WebSocket mWebSocket; + private NodeErrorListener mErrorListener; + + /** + * Constructor + * + * @param url URL of the WebSocket + */ + public WebsocketWorkerThread(String url){ + try { + WebSocketFactory factory = new WebSocketFactory().setConnectionTimeout(TIMEOUT); + + if(DEBUG){ + SSLContext context = NaiveSSLContext.getInstance("TLS"); + + // Set the custom SSL context. + factory.setSSLContext(context); + } + + mWebSocket = factory.createSocket(url); + } catch (IOException e) { + System.out.println("IOException. Msg: "+e.getMessage()); + } catch(NullPointerException e){ + System.out.println("NullPointerException at WebsocketWorkerThreas. Msg: "+e.getMessage()); + } catch (NoSuchAlgorithmException e) { + System.out.println("NoSuchAlgorithmException. Msg: "+e.getMessage()); + } + } + + /** + * Constructor with connection error listener. + * + * @param url URL of the WebSocket + * @param errorListener a class implementing the NodeErrorListener interface. This + * should be implemented by the party interested in being notified + * about the failure of the connection. + */ + public WebsocketWorkerThread(String url, NodeErrorListener errorListener){ + try { + WebSocketFactory factory = new WebSocketFactory().setConnectionTimeout(TIMEOUT); + + if(DEBUG){ + SSLContext context = NaiveSSLContext.getInstance("TLS"); + + // Set the custom SSL context. + factory.setSSLContext(context); + } + + mWebSocket = factory.createSocket(url); + mErrorListener = errorListener; + } catch (IOException e) { + System.out.println("IOException. Msg: "+e.getMessage()); + } catch(NullPointerException e){ + System.out.println("NullPointerException at WebsocketWorkerThreas. Msg: "+e.getMessage()); + } catch (NoSuchAlgorithmException e) { + System.out.println("NoSuchAlgorithmException. Msg: "+e.getMessage()); + } + } + + /** + * Method call when the thread is started. + */ + @Override + public void run() { + try { + mWebSocket.connect(); + } catch (WebSocketException e) { + System.out.println("WebSocketException. Msg: "+e.getMessage()); + mErrorListener.onError(new BaseResponse.Error(e.getMessage())); + } + } + + /** + * Add a WebSocketListener to the thread that will run. This should be implemented by the party + * interested in being notified about the response value of a request. + * + * @param listener listener implemented to be notified when the socket get a response from the + * node + */ + public void addListener(WebSocketListener listener){ + mWebSocket.addListener(listener); + } +} \ No newline at end of file diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/errors/RepeatedRequestIdException.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/errors/RepeatedRequestIdException.java new file mode 100644 index 0000000..74c19e3 --- /dev/null +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/errors/RepeatedRequestIdException.java @@ -0,0 +1,12 @@ +package de.bitsharesmunich.graphenej.errors; + +/** + * Created by nelson on 6/27/17. + */ + +public class RepeatedRequestIdException extends Exception { + + public RepeatedRequestIdException(String message){ + super(message); + } +} diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/interfaces/NodeErrorListener.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/interfaces/NodeErrorListener.java new file mode 100644 index 0000000..f0386b7 --- /dev/null +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/interfaces/NodeErrorListener.java @@ -0,0 +1,10 @@ +package de.bitsharesmunich.graphenej.interfaces; + +import de.bitsharesmunich.graphenej.models.BaseResponse; + +/** + * Interface to be implemented by any listener to network errors. + */ +public interface NodeErrorListener { + void onError(BaseResponse.Error error); +} diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/models/BaseResponse.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/models/BaseResponse.java index 3801777..d4bb18c 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/models/BaseResponse.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/models/BaseResponse.java @@ -4,7 +4,7 @@ package de.bitsharesmunich.graphenej.models; * Created by nelson on 11/12/16. */ public class BaseResponse { - public int id; + public long id; public Error error; public static class Error { diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/models/SubscriptionResponse.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/models/SubscriptionResponse.java index 4e27b55..3cb5f78 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/models/SubscriptionResponse.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/models/SubscriptionResponse.java @@ -51,11 +51,12 @@ public class SubscriptionResponse { public static final String KEY_METHOD = "method"; public static final String KEY_PARAMS = "params"; + public int id; public String method; public List params; /** - * Deserializer class that is used to parse and deserialize subscription responses in a partial way, + * Inner static class used to parse and deserialize subscription responses in a partial way, * depending on the amount of SubscriptionListeners we might have registered. * * The rationale behind these architecture is to avoid wasting computational resources parsing unneeded @@ -82,6 +83,11 @@ public class SubscriptionResponse { listenerTypeCount = new HashMap<>(); } + /** + * Adds a subscription listener to the list. + * @param subscriptionListener: Class implementing the {@see SubscriptionListener} interface + * to be added to the list. + */ public void addSubscriptionListener(SubscriptionListener subscriptionListener){ int currentCount = 0; if(listenerTypeCount.containsKey(subscriptionListener.getInterestObjectType())){ @@ -91,10 +97,19 @@ public class SubscriptionResponse { this.mListeners.add(subscriptionListener); } + /** + * Retrieves the full list of SubscriptionListeners registered. + * @return + */ public List getSubscriptionListeners(){ return this.mListeners; } + /** + * Removes a subscription listener to the list. + * @param subscriptionListener: Class implementing the {@see SubscriptionListener} interface + * to be removed from the list. + */ public void removeSubscriptionListener(SubscriptionListener subscriptionListener){ int currentCount = listenerTypeCount.get(subscriptionListener.getInterestObjectType()); if(currentCount != 0){ @@ -105,6 +120,14 @@ public class SubscriptionResponse { this.mListeners.remove(subscriptionListener); } + /** + * Removes all registered subscription listeners + */ + public void clearAllSubscriptionListeners(){ + this.mListeners.clear(); + this.listenerTypeCount.clear(); + } + @Override public SubscriptionResponse deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { SubscriptionResponse response = new SubscriptionResponse(); diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/objects/Memo.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/objects/Memo.java index e84cb51..a618ba2 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/objects/Memo.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/objects/Memo.java @@ -1,17 +1,25 @@ package de.bitsharesmunich.graphenej.objects; import com.google.common.primitives.Bytes; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; + import de.bitsharesmunich.graphenej.Address; import de.bitsharesmunich.graphenej.PublicKey; import de.bitsharesmunich.graphenej.Util; import de.bitsharesmunich.graphenej.errors.ChecksumException; +import de.bitsharesmunich.graphenej.errors.MalformedAddressException; import de.bitsharesmunich.graphenej.interfaces.ByteSerializable; import de.bitsharesmunich.graphenej.interfaces.JsonSerializable; +import de.bitsharesmunich.graphenej.operations.TransferOperation; + import org.bitcoinj.core.ECKey; import org.spongycastle.math.ec.ECPoint; +import java.lang.reflect.Type; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; @@ -269,4 +277,30 @@ public class Memo implements ByteSerializable, JsonSerializable { } return memoObject; } + + /** + * Class used to deserialize a memo + */ + public static class MemoDeserializer implements JsonDeserializer { + + @Override + public Memo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + JsonObject jsonObject = json.getAsJsonObject(); + String fromAddress = jsonObject.get(KEY_FROM).getAsString(); + String toAddress = jsonObject.get(KEY_TO).getAsString(); + long nonce = jsonObject.get(KEY_NONCE).getAsLong(); + String msg = jsonObject.get(KEY_MESSAGE).getAsString(); + + Memo memo = null; + try{ + Address from = new Address(fromAddress); + Address to = new Address(toAddress); + byte[] message = Util.hexToBytes(msg); + memo = new Memo(from, to, nonce, message); + }catch(MalformedAddressException e){ + System.out.println("MalformedAddressException. Msg: "+e.getMessage()); + } + return memo; + } + } } \ No newline at end of file diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/operations/AccountUpdateOperation.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/operations/AccountUpdateOperation.java index 5798f2e..66a7310 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/operations/AccountUpdateOperation.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/operations/AccountUpdateOperation.java @@ -77,7 +77,7 @@ public class AccountUpdateOperation extends BaseOperation { JsonObject accountUpdate = new JsonObject(); accountUpdate.add(KEY_FEE, fee.toJsonObject()); - accountUpdate.addProperty(KEY_ACCOUNT, account.toJsonString()); + accountUpdate.addProperty(KEY_ACCOUNT, account.getObjectId()); if(owner.isSet()) accountUpdate.add(KEY_OWNER, owner.toJsonObject()); if(active.isSet()) diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/operations/LimitOrderCreateOperation.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/operations/LimitOrderCreateOperation.java index 195682a..df3a5f7 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/operations/LimitOrderCreateOperation.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/operations/LimitOrderCreateOperation.java @@ -79,7 +79,7 @@ public class LimitOrderCreateOperation extends BaseOperation { JsonObject jsonObject = new JsonObject(); if(fee != null) jsonObject.add(KEY_FEE, fee.toJsonObject()); - jsonObject.addProperty(KEY_SELLER, seller.toJsonString()); + jsonObject.addProperty(KEY_SELLER, seller.getObjectId()); jsonObject.add(KEY_AMOUNT_TO_SELL, amountToSell.toJsonObject()); jsonObject.add(KEY_MIN_TO_RECEIVE, minToReceive.toJsonObject()); diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/operations/TransferOperation.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/operations/TransferOperation.java index 8eee847..89d5b14 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/operations/TransferOperation.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/operations/TransferOperation.java @@ -118,8 +118,8 @@ public class TransferOperation extends BaseOperation { JsonObject jsonObject = new JsonObject(); if(fee != null) jsonObject.add(KEY_FEE, fee.toJsonObject()); - jsonObject.addProperty(KEY_FROM, from.toJsonString()); - jsonObject.addProperty(KEY_TO, to.toJsonString()); + jsonObject.addProperty(KEY_FROM, from.getObjectId()); + jsonObject.addProperty(KEY_TO, to.getObjectId()); jsonObject.add(KEY_AMOUNT, amount.toJsonObject()); jsonObject.add(KEY_MEMO, memo.toJsonObject()); jsonObject.add(KEY_EXTENSIONS, new JsonArray()); @@ -191,6 +191,13 @@ public class TransferOperation extends BaseOperation { UserAccount from = new UserAccount(jsonObject.get(KEY_FROM).getAsString()); UserAccount to = new UserAccount(jsonObject.get(KEY_TO).getAsString()); TransferOperation transfer = new TransferOperation(from, to, amount, fee); + + // If the transfer had a memo, deserialize it + if(jsonObject.has(KEY_MEMO)){ + Memo memo = context.deserialize(jsonObject.get(KEY_MEMO), Memo.class); + transfer.setMemo(memo); + } + return transfer; } } diff --git a/graphenej/src/test/java/de/bitsharesmunich/graphenej/api/GetRelativeAccountHistoryTest.java b/graphenej/src/test/java/de/bitsharesmunich/graphenej/api/GetRelativeAccountHistoryTest.java new file mode 100644 index 0000000..c3ab49e --- /dev/null +++ b/graphenej/src/test/java/de/bitsharesmunich/graphenej/api/GetRelativeAccountHistoryTest.java @@ -0,0 +1,72 @@ +package de.bitsharesmunich.graphenej.api; + +import com.neovisionaries.ws.client.WebSocketException; + +import org.junit.Test; + +import java.util.List; +import de.bitsharesmunich.graphenej.UserAccount; +import de.bitsharesmunich.graphenej.interfaces.WitnessResponseListener; +import de.bitsharesmunich.graphenej.models.BaseResponse; +import de.bitsharesmunich.graphenej.models.HistoricalTransfer; +import de.bitsharesmunich.graphenej.models.WitnessResponse; +import de.bitsharesmunich.graphenej.operations.TransferOperation; + +/** + * Created by nelson on 9/25/17. + */ + +public class GetRelativeAccountHistoryTest extends BaseApiTest { + private final String TAG = this.getClass().getName(); + + private int HISTORICAL_TRANSFER_BATCH_SIZE = 10; + private final UserAccount bilthon_7 = new UserAccount("1.2.140994"); + private int historicalTransferCount; + + @Test + public void testRelativeAccountHistory(){ + int start = 0; + GetRelativeAccountHistory mGetRelativeAccountHistory = new GetRelativeAccountHistory(bilthon_7, 0, HISTORICAL_TRANSFER_BATCH_SIZE, start, mTransferHistoryListener); + mWebSocket.addListener(mGetRelativeAccountHistory); + + try{ + mWebSocket.connect(); + synchronized (this){ + wait(); + } + }catch (WebSocketException e) { + System.out.println("WebSocketException. Msg: " + e.getMessage()); + } catch (InterruptedException e) { + System.out.println("InterruptedException. Msg: "+e.getMessage()); + } + } + + /** + * Callback activated once we get a response back from the full node telling us about the + * transfer history of the current account. + */ + private WitnessResponseListener mTransferHistoryListener = new WitnessResponseListener() { + + @Override + public void onSuccess(WitnessResponse response) { + System.out.println("mTransferHistoryListener.onSuccess"); + historicalTransferCount++; + WitnessResponse> resp = response; + for(HistoricalTransfer historicalTransfer : resp.result){ + if(historicalTransfer.getOperation() != null){ + System.out.println("Got transfer operation!"); + TransferOperation transferOperation = historicalTransfer.getOperation(); + System.out.println(String.format("%s - > %s, memo: %s", + transferOperation.getFrom().getObjectId(), + transferOperation.getTo().getObjectId(), + transferOperation.getMemo() == null ? "*" : transferOperation.getMemo().getStringMessage())); + } + } + } + + @Override + public void onError(BaseResponse.Error error) { + System.out.println("onError. Msg: "+error.message); + } + }; +} diff --git a/graphenej/src/test/java/de/bitsharesmunich/graphenej/api/SubscriptionMessagesHubTest.java b/graphenej/src/test/java/de/bitsharesmunich/graphenej/api/SubscriptionMessagesHubTest.java index 0311180..9d4e4b5 100644 --- a/graphenej/src/test/java/de/bitsharesmunich/graphenej/api/SubscriptionMessagesHubTest.java +++ b/graphenej/src/test/java/de/bitsharesmunich/graphenej/api/SubscriptionMessagesHubTest.java @@ -5,11 +5,13 @@ import com.neovisionaries.ws.client.WebSocketException; import org.junit.Test; import java.io.Serializable; -import java.util.ArrayList; import java.util.List; +import java.util.Timer; +import java.util.TimerTask; import de.bitsharesmunich.graphenej.ObjectType; import de.bitsharesmunich.graphenej.Transaction; +import de.bitsharesmunich.graphenej.interfaces.NodeErrorListener; import de.bitsharesmunich.graphenej.interfaces.SubscriptionListener; import de.bitsharesmunich.graphenej.interfaces.WitnessResponseListener; import de.bitsharesmunich.graphenej.models.BaseResponse; @@ -19,30 +21,94 @@ import de.bitsharesmunich.graphenej.models.SubscriptionResponse; import de.bitsharesmunich.graphenej.models.WitnessResponse; /** - * Created by nelson on 4/25/17. + * Class used to encapsulate all tests that relate to the {@see SubscriptionMessagesHub} class. */ public class SubscriptionMessagesHubTest extends BaseApiTest { private SubscriptionMessagesHub mMessagesHub; - private WitnessResponseListener mErrorListener = new WitnessResponseListener() { - @Override - public void onSuccess(WitnessResponse response) { - System.out.println("onSuccess"); - } + /** + * Error listener + */ + private NodeErrorListener mErrorListener = new NodeErrorListener() { @Override public void onError(BaseResponse.Error error) { System.out.println("onError"); } }; + /** + * Testing the subscription and unsubscription features. + * + * The test is deemed successful if no exception is thown and the messages indeed + * are cancelled. + */ + @Test + public void testSubscribeUnsubscribe(){ + /** + * Task that will send a 'cancel_all_subscriptions' API message. + */ + TimerTask unsubscribeTask = new TimerTask() { + @Override + public void run() { + System.out.println("Cancelling all subscriptions"); + mMessagesHub.cancelSubscriptions(); + } + }; + + TimerTask resubscribeTask = new TimerTask() { + @Override + public void run() { + System.out.println("Resubscribing.."); + mMessagesHub.resubscribe(); + } + }; + + /** + * Task that will just finish the test. + */ + TimerTask shutdownTask = new TimerTask() { + + @Override + public void run() { + System.out.println("Finish test"); + synchronized (SubscriptionMessagesHubTest.this){ + SubscriptionMessagesHubTest.this.notifyAll(); + } + } + }; + + try{ + mMessagesHub = new SubscriptionMessagesHub("", "", true, mErrorListener); + mWebSocket.addListener(mMessagesHub); + mWebSocket.connect(); + + Timer timer = new Timer(); + timer.schedule(unsubscribeTask, 5000); + timer.schedule(resubscribeTask, 10000); + timer.schedule(shutdownTask, 20000); + + // Holding this thread while we get update notifications + synchronized (this){ + wait(); + } + } catch (InterruptedException e) { + System.out.println("InterruptedException. Msg: "+e.getMessage()); + } catch (WebSocketException e) { + System.out.println("WebSocketException. Msg: " + e.getMessage()); + } + } + + /** + * This test will register a {@see SubscriptionListener} and wait for an amount equal to MAX_MESSAGES + * of {@see DynamicGlobalProperties} objects to be returned. + * + * The test will be deemed successfull if no errors arise in the meantime. + */ @Test public void testGlobalPropertiesDeserializer(){ - ArrayList interestingObjects = new ArrayList(); - interestingObjects.add(ObjectType.TRANSACTION_OBJECT); - interestingObjects.add(ObjectType.DYNAMIC_GLOBAL_PROPERTY_OBJECT); try{ - mMessagesHub = new SubscriptionMessagesHub("", "", interestingObjects, mErrorListener); + mMessagesHub = new SubscriptionMessagesHub("", "", true, mErrorListener); mMessagesHub.addSubscriptionListener(new SubscriptionListener() { private int MAX_MESSAGES = 10; private int messageCounter = 0; @@ -107,6 +173,8 @@ public class SubscriptionMessagesHubTest extends BaseApiTest { /** * This is a basic test that will only display a count of operations per received broadcasted transactions. + * + * The test will be deemed successfull if we get to receive MAX_MESSAGES transaction objects without errors. */ @Test public void testBroadcastedTransactionDeserializer(){ diff --git a/graphenej/src/test/java/de/bitsharesmunich/graphenej/api/android/NodeConnectionTest.java b/graphenej/src/test/java/de/bitsharesmunich/graphenej/api/android/NodeConnectionTest.java new file mode 100644 index 0000000..226c92d --- /dev/null +++ b/graphenej/src/test/java/de/bitsharesmunich/graphenej/api/android/NodeConnectionTest.java @@ -0,0 +1,1004 @@ +package de.bitsharesmunich.graphenej.api.android; + +import com.google.common.primitives.UnsignedLong; +import com.neovisionaries.ws.client.WebSocket; +import com.neovisionaries.ws.client.WebSocketException; +import com.neovisionaries.ws.client.WebSocketFactory; + +import org.bitcoinj.core.ECKey; +import org.junit.Test; + +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Date; +import java.util.Timer; +import java.util.TimerTask; + +import javax.net.ssl.SSLContext; + +import de.bitsharesmunich.graphenej.Address; +import de.bitsharesmunich.graphenej.Asset; +import de.bitsharesmunich.graphenej.AssetAmount; +import de.bitsharesmunich.graphenej.BaseOperation; +import de.bitsharesmunich.graphenej.BrainKey; +import de.bitsharesmunich.graphenej.Transaction; +import de.bitsharesmunich.graphenej.UserAccount; +import de.bitsharesmunich.graphenej.api.GetAccountBalances; +import de.bitsharesmunich.graphenej.api.GetAccountByName; +import de.bitsharesmunich.graphenej.api.GetAccounts; +import de.bitsharesmunich.graphenej.api.GetAllAssetHolders; +import de.bitsharesmunich.graphenej.api.GetBlockHeader; +import de.bitsharesmunich.graphenej.api.GetKeyReferences; +import de.bitsharesmunich.graphenej.api.GetLimitOrders; +import de.bitsharesmunich.graphenej.api.GetMarketHistory; +import de.bitsharesmunich.graphenej.api.GetObjects; +import de.bitsharesmunich.graphenej.api.GetRelativeAccountHistory; +import de.bitsharesmunich.graphenej.api.GetRequiredFees; +import de.bitsharesmunich.graphenej.api.GetTradeHistory; +import de.bitsharesmunich.graphenej.api.ListAssets; +import de.bitsharesmunich.graphenej.api.LookupAccounts; +import de.bitsharesmunich.graphenej.api.LookupAssetSymbols; +import de.bitsharesmunich.graphenej.api.TransactionBroadcastSequence; +import de.bitsharesmunich.graphenej.errors.MalformedAddressException; +import de.bitsharesmunich.graphenej.errors.RepeatedRequestIdException; +import de.bitsharesmunich.graphenej.interfaces.WitnessResponseListener; +import de.bitsharesmunich.graphenej.models.BaseResponse; +import de.bitsharesmunich.graphenej.models.WitnessResponse; +import de.bitsharesmunich.graphenej.operations.TransferOperation; +import de.bitsharesmunich.graphenej.operations.TransferOperationBuilder; +import de.bitsharesmunich.graphenej.test.NaiveSSLContext; + +/** + * Created by nelson on 6/26/17. + */ +public class NodeConnectionTest { + private String NODE_URL_1 = System.getenv("NODE_URL_1"); + private String NODE_URL_2 = System.getenv("NODE_URL_2"); + private String NODE_URL_3 = System.getenv("NODE_URL_3"); + private String NODE_URL_4 = System.getenv("NODE_URL_4"); + private String TEST_ACCOUNT_BRAIN_KEY = System.getenv("TEST_ACCOUNT_BRAIN_KEY"); + private String ACCOUNT_ID_1 = System.getenv("ACCOUNT_ID_1"); + private String ACCOUNT_ID_2 = System.getenv("ACCOUNT_ID_2"); + private String ACCOUNT_NAME = System.getenv("ACCOUNT_NAME"); + private long BlOCK_TEST_NUMBER = Long.parseLong(System.getenv("BlOCK_TEST_NUMBER")); + private Asset BTS = new Asset("1.3.0"); + private Asset BLOCKPAY = new Asset("1.3.1072"); + private Asset BITDOLAR = new Asset("1.3.121"); //USD Smartcoin + private Asset BITEURO = new Asset("1.3.120"); //EUR Smartcoin + private NodeConnection nodeConnection; + + private TimerTask scheduleTask = new TimerTask() { + @Override + public void run() { + System.out.println("Adding request here"); + try{ + nodeConnection.addRequestHandler(new GetAccounts("1.2.100", false, new WitnessResponseListener(){ + + @Override + public void onSuccess(WitnessResponse response) { + System.out.println("getAccounts.onSuccess"); + } + + @Override + public void onError(BaseResponse.Error error) { + System.out.println("getAccounts.onError. Msg: "+ error.message); + } + })); + }catch(RepeatedRequestIdException e){ + System.out.println("RepeatedRequestIdException. Msg: "+e.getMessage()); + } + } + }; + + private TimerTask releaseTask = new TimerTask() { + @Override + public void run() { + System.out.println("Releasing lock!"); + synchronized (NodeConnectionTest.this){ + NodeConnectionTest.this.notifyAll(); + } + } + }; + + @Test + public void testNodeConnection(){ + nodeConnection = NodeConnection.getInstance(); + nodeConnection.addNodeUrl(NODE_URL_1); + nodeConnection.connect("", "", true, mErrorListener); + + Timer timer = new Timer(); + timer.schedule(scheduleTask, 5000); + timer.schedule(releaseTask, 30000); + + try{ + // Holding this thread while we get update notifications + synchronized (this){ + wait(); + } + }catch(InterruptedException e){ + System.out.println("InterruptedException. Msg: "+e.getMessage()); + } + } + + @Test + /** + * Test for NodeConnection's addNodeUrl and addNodeUrls working together. + * + * Need to setup the NODE_URL_(1 to 4) env to work. Some of the nodes may have invalid nodes + * websockets URL just to test the hop. + * + */ + public void testNodeHopFeature(){ + nodeConnection = NodeConnection.getInstance(); + //nodeConnection.addNodeUrl(NODE_URL_4); + //Test adding a "sublist" + ArrayList urlList = new ArrayList(){{ + add(NODE_URL_3); + add(NODE_URL_3); + }}; + //nodeConnection.addNodeUrls(urlList); + nodeConnection.addNodeUrl(NODE_URL_1); + + nodeConnection.connect("", "", true, mErrorListener); + + Timer timer = new Timer(); + timer.schedule(scheduleTask, 5000); + timer.schedule(releaseTask, 30000); + + try{ + // Holding this thread while we get update notifications + synchronized (this){ + wait(); + } + }catch(InterruptedException e){ + System.out.println("InterruptedException. Msg: "+e.getMessage()); + } + } + + /** + * Test for GetAccountBalances Handler. + * + * Request balances for a valid account (Need to setup the ACCOUNT_ID_1 env with desired account id) + * + */ + @Test + public void testGetAccountBalancesRequest(){ + nodeConnection = NodeConnection.getInstance(); + nodeConnection.addNodeUrl(NODE_URL_1); + nodeConnection.connect("", "", false, mErrorListener); + + System.out.println("Adding GetAccountBalances here"); + try{ + UserAccount userAccount = new UserAccount(ACCOUNT_ID_1); + ArrayList assetList = new ArrayList<>(); + assetList.add(BTS); + assetList.add(BITDOLAR); + assetList.add(BITEURO); + System.out.println("Test: Request to discrete asset list"); + nodeConnection.addRequestHandler(new GetAccountBalances(userAccount, assetList, false, new WitnessResponseListener(){ + @Override + public void onSuccess(WitnessResponse response) { + System.out.println("getAccountBalances.onSuccess"); + } + + @Override + public void onError(BaseResponse.Error error) { + System.out.println("getAccountBalances.onError. Msg: "+ error.message); + } + })); + }catch(RepeatedRequestIdException e){ + System.out.println("RepeatedRequestIdException. Msg: "+e.getMessage()); + } + + try{ + UserAccount userAccount = new UserAccount(ACCOUNT_ID_1); + System.out.println("Test: Request to all account' assets balance"); + nodeConnection.addRequestHandler(new GetAccountBalances(userAccount, null, false, new WitnessResponseListener(){ + @Override + public void onSuccess(WitnessResponse response) { + System.out.println("getAccountBalances.onSuccess"); + } + + @Override + public void onError(BaseResponse.Error error) { + System.out.println("getAccountBalances.onError. Msg: "+ error.message); + } + })); + }catch(RepeatedRequestIdException e){ + System.out.println("RepeatedRequestIdException. Msg: "+e.getMessage()); + } + + try{ + // Holding this thread while we get update notifications + synchronized (this){ + wait(); + } + }catch(InterruptedException e){ + System.out.println("InterruptedException. Msg: "+e.getMessage()); + } + } + + @Test + /** + * Test for GetAccountByName Handler. + * + * Request for a valid account name by name (Need to setup the ACCOUNT_NAME env with desired + * account name) + * + */ + public void testGetAccountByNameRequest(){ + nodeConnection = NodeConnection.getInstance(); + nodeConnection.addNodeUrl(NODE_URL_1); + nodeConnection.connect("", "", false, mErrorListener); + + System.out.println("Adding GetAccountByName here"); + try{ + nodeConnection.addRequestHandler(new GetAccountByName(ACCOUNT_NAME, false, new WitnessResponseListener(){ + @Override + public void onSuccess(WitnessResponse response) { + System.out.println("GetAccountByName.onSuccess"); + } + + @Override + public void onError(BaseResponse.Error error) { + System.out.println("GetAccountByName.onError. Msg: "+ error.message); + } + })); + }catch(RepeatedRequestIdException e){ + System.out.println("RepeatedRequestIdException. Msg: "+e.getMessage()); + } + + try{ + // Holding this thread while we get update notifications + synchronized (this){ + wait(); + } + }catch(InterruptedException e){ + System.out.println("InterruptedException. Msg: "+e.getMessage()); + } + } + + /** + * Test for GetAccounts Handler. + * + * Request for a valid account name by name (Need to setup the ACCOUNT_NAME env with desired + * account name) + * + */ + public void testGetAccountsRequest(){ + nodeConnection = NodeConnection.getInstance(); + nodeConnection.addNodeUrl(NODE_URL_1); + + ArrayList accountList = new ArrayList(){{ + add(new UserAccount(ACCOUNT_ID_1)); + add(new UserAccount(ACCOUNT_ID_2)); + }}; + + nodeConnection.connect("", "", false, mErrorListener); + + System.out.println("Adding GetAccounts for one Account ID."); + try{ + nodeConnection.addRequestHandler(new GetAccounts(ACCOUNT_ID_1, false, new WitnessResponseListener(){ + @Override + public void onSuccess(WitnessResponse response) { + System.out.println("GetAccounts.onSuccess"); + } + + @Override + public void onError(BaseResponse.Error error) { + System.out.println("GetAccounts.onError. Msg: "+ error.message); + } + })); + }catch(RepeatedRequestIdException e){ + System.out.println("RepeatedRequestIdException. Msg: "+e.getMessage()); + } + + System.out.println("Adding GetAccounts for a list of Account IDs."); + try{ + nodeConnection.addRequestHandler(new GetAccounts(accountList, false, new WitnessResponseListener(){ + @Override + public void onSuccess(WitnessResponse response) { + System.out.println("GetAccounts.onSuccess"); + } + + @Override + public void onError(BaseResponse.Error error) { + System.out.println("GetAccounts.onError. Msg: "+ error.message); + } + })); + }catch(RepeatedRequestIdException e){ + System.out.println("RepeatedRequestIdException. Msg: "+e.getMessage()); + } + + + try{ + // Holding this thread while we get update notifications + synchronized (this){ + wait(); + } + }catch(InterruptedException e){ + System.out.println("InterruptedException. Msg: "+e.getMessage()); + } + } + + /** + * Test for GetAllAssetHolders Handler. + * + */ + @Test + public void testGetAllAssetHoldersRequest(){ + nodeConnection = NodeConnection.getInstance(); + nodeConnection.addNodeUrl(NODE_URL_1); + nodeConnection.connect("", "", false, mErrorListener); + + System.out.println("Adding GetAllAssetHolders request"); + try{ + nodeConnection.addRequestHandler(new GetAllAssetHolders(false, new WitnessResponseListener(){ + @Override + public void onSuccess(WitnessResponse response) { + System.out.println("GetAllAssetHolders.onSuccess"); + } + + @Override + public void onError(BaseResponse.Error error) { + System.out.println("GetAllAssetHolders.onError. Msg: "+ error.message); + } + })); + }catch(RepeatedRequestIdException e){ + System.out.println("RepeatedRequestIdException. Msg: "+e.getMessage()); + } + + try{ + // Holding this thread while we get update notifications + synchronized (this){ + wait(); + } + }catch(InterruptedException e){ + System.out.println("InterruptedException. Msg: "+e.getMessage()); + } + } + + /** + * Test for GetBlockHeader Handler. + * + * Request for a valid account block header (Need to setup the BlOCK_TEST_NUMBER env with desired + * block height) + */ + @Test + public void testGetBlockHeaderRequest(){ + nodeConnection = NodeConnection.getInstance(); + nodeConnection.addNodeUrl(NODE_URL_1); + nodeConnection.connect("", "", false, mErrorListener); + + + System.out.println("Adding GetBlockHeader request"); + try{ + nodeConnection.addRequestHandler(new GetBlockHeader(BlOCK_TEST_NUMBER,false, new WitnessResponseListener(){ + @Override + public void onSuccess(WitnessResponse response) { + System.out.println("GetBlockHeader.onSuccess"); + } + + @Override + public void onError(BaseResponse.Error error) { + System.out.println("GetBlockHeader.onError. Msg: "+ error.message); + } + })); + }catch(RepeatedRequestIdException e){ + System.out.println("RepeatedRequestIdException. Msg: "+e.getMessage()); + } + + + try{ + // Holding this thread while we get update notifications + synchronized (this){ + wait(); + } + }catch(InterruptedException e){ + System.out.println("InterruptedException. Msg: "+e.getMessage()); + } + } + + /** + * Test for GetKeyReferences Handler. + * + */ + @Test + public void testGetKeyReferencesRequest(){ + nodeConnection = NodeConnection.getInstance(); + nodeConnection.addNodeUrl(NODE_URL_1); + nodeConnection.connect("", "", false, mErrorListener); + + Address address1 = null; + Address address2 = null; + Address address3 = null; + try { + address1 = new Address("BTS8RiFgs8HkcVPVobHLKEv6yL3iXcC9SWjbPVS15dDAXLG9GYhnY"); + address2 = new Address("BTS8RiFgs8HkcVPVobHLKEv6yL3iXcC9SWjbPVS15dDAXLG9GYhnY"); + address3 = new Address("BTS8RiFgs8HkcVPVobHLKEv6yL3iXcC9SWjbPVS15dDAXLG9GYp00"); + } catch (MalformedAddressException e) { + System.out.println("MalformedAddressException. Msg: " + e.getMessage()); + } + + ArrayList
addresses = new ArrayList<>(); + addresses.add(address1); + addresses.add(address2); + addresses.add(address3); + + // Test with the one address constructor + System.out.println("Adding GetKeyReferences one address request (One address)"); + try{ + nodeConnection.addRequestHandler(new GetKeyReferences(address1, false, new WitnessResponseListener(){ + @Override + public void onSuccess(WitnessResponse response) { + System.out.println("GetKeyReferences.onSuccess"); + } + + @Override + public void onError(BaseResponse.Error error) { + System.out.println("GetKeyReferences.onError. Msg: "+ error.message); + } + })); + }catch(RepeatedRequestIdException e){ + System.out.println("RepeatedRequestIdException. Msg: "+e.getMessage()); + } + + // Test with the list of addresses constructor + System.out.println("Adding GetKeyReferences address request (List of Addresses)"); + try{ + nodeConnection.addRequestHandler(new GetKeyReferences(addresses, false, new WitnessResponseListener(){ + @Override + public void onSuccess(WitnessResponse response) { + System.out.println("GetKeyReferences.onSuccess"); + } + + @Override + public void onError(BaseResponse.Error error) { + System.out.println("GetKeyReferences.onError. Msg: "+ error.message); + } + })); + }catch(RepeatedRequestIdException e){ + System.out.println("RepeatedRequestIdException. Msg: "+e.getMessage()); + } + + try{ + // Holding this thread while we get update notifications + synchronized (this){ + wait(); + } + }catch(InterruptedException e){ + System.out.println("InterruptedException. Msg: "+e.getMessage()); + } + } + + /** + * Test for GetLimitOrders Handler. + * + * Request for a limit orders between two assets + */ + @Test + public void testGetLimitOrdersRequest(){ + nodeConnection = NodeConnection.getInstance(); + nodeConnection.addNodeUrl(NODE_URL_1); + nodeConnection.connect("", "", false, mErrorListener); + + String asset_sold_id = BLOCKPAY.getBitassetId(); + String asset_purchased_id = BTS.getBitassetId(); + int limit = 10; + + System.out.println("Adding GetLimitOrders request"); + try{ + nodeConnection.addRequestHandler(new GetLimitOrders(asset_sold_id, asset_purchased_id, limit, true, new WitnessResponseListener(){ + @Override + public void onSuccess(WitnessResponse response) { + System.out.println("GetLimitOrders.onSuccess"); + } + + @Override + public void onError(BaseResponse.Error error) { + System.out.println("GetLimitOrders.onError. Msg: "+ error.message); + } + })); + }catch(RepeatedRequestIdException e){ + System.out.println("RepeatedRequestIdException. Msg: "+e.getMessage()); + } + + + try{ + // Holding this thread while we get update notifications + synchronized (this){ + wait(); + } + }catch(InterruptedException e){ + System.out.println("InterruptedException. Msg: "+e.getMessage()); + } + } + + + /** + * Test for GetMarketHistory Handler. + * + * Request for market history of a base asset compared to a quote asset. + */ + @Test + public void testGetMarketHistoryRequest(){ + nodeConnection = NodeConnection.getInstance(); + nodeConnection.addNodeUrl(NODE_URL_1); + nodeConnection.connect("", "", false, mErrorListener); + + Asset asset_base = BLOCKPAY; + Asset asset_quote = BTS; + //the time interval of the bucket in seconds + long bucket = 3600; + + //datetime of of the most recent operation to retrieve + Date start = new Date(); + //datetime of the the earliest operation to retrieve + //07/16/2017 1:33pm + Date end = new Date(1500211991); + + System.out.println("Adding GetMarketHistory request"); + try{ + nodeConnection.addRequestHandler(new GetMarketHistory(asset_base, asset_quote, bucket, start, end, true, new WitnessResponseListener(){ + @Override + public void onSuccess(WitnessResponse response) { + System.out.println("GetMarketHistory.onSuccess"); + } + + @Override + public void onError(BaseResponse.Error error) { + System.out.println("GetMarketHistory.onError. Msg: "+ error.message); + } + })); + }catch(RepeatedRequestIdException e){ + System.out.println("RepeatedRequestIdException. Msg: "+e.getMessage()); + } + + + try{ + // Holding this thread while we get update notifications + synchronized (this){ + wait(); + } + }catch(InterruptedException e){ + System.out.println("InterruptedException. Msg: "+e.getMessage()); + } + } + + /** + * Test for GetObjects Handler. + * + * Request for a limit orders between two assets + */ + @Test + public void testGetObjectsRequest(){ + nodeConnection = NodeConnection.getInstance(); + nodeConnection.addNodeUrl(NODE_URL_1); + nodeConnection.connect("", "", false, mErrorListener); + + String asset_sold_id = BLOCKPAY.getBitassetId(); + String asset_purchased_id = BTS.getBitassetId(); + int limit = 10; + + ArrayList objectList = new ArrayList(){{ + add(BLOCKPAY.getBitassetId()); + add(BTS.getBitassetId()); + add(BITEURO.getBitassetId()); + }}; + + System.out.println("Adding GetObjects request"); + try{ + nodeConnection.addRequestHandler(new GetObjects(objectList, true, new WitnessResponseListener(){ + @Override + public void onSuccess(WitnessResponse response) { + System.out.println("GetObjects.onSuccess"); + } + + @Override + public void onError(BaseResponse.Error error) { + System.out.println("GetObjects.onError. Msg: "+ error.message); + } + })); + }catch(RepeatedRequestIdException e){ + System.out.println("RepeatedRequestIdException. Msg: "+e.getMessage()); + } + + Timer timer = new Timer(); + timer.schedule(releaseTask, 30000); + + try{ + // Holding this thread while we get update notifications + synchronized (this){ + wait(); + } + }catch(InterruptedException e){ + System.out.println("InterruptedException. Msg: "+e.getMessage()); + } + } + + /** + * Test for GetRelativeAccount Handler. + * + * Request for the transaction history of a user account. + */ + @Test + public void testGetRelativeAccountHistoryRequest(){ + nodeConnection = NodeConnection.getInstance(); + nodeConnection.addNodeUrl(NODE_URL_1); + nodeConnection.connect("", "", false, mErrorListener); + + UserAccount userAccount = new UserAccount(ACCOUNT_ID_1); + + //Sequence number of earliest operation + int stop = 10; + int limit = 50; + //Sequence number of the most recent operation to retrieve + int start = 50; + + System.out.println("Adding GetRelativeAccountHistory request"); + try{ + nodeConnection.addRequestHandler(new GetRelativeAccountHistory(userAccount, stop, limit, start, true, new WitnessResponseListener(){ + @Override + public void onSuccess(WitnessResponse response) { + System.out.println("GetRelativeAccountHistory.onSuccess"); + } + + @Override + public void onError(BaseResponse.Error error) { + System.out.println("GetRelativeAccountHistory.onError. Msg: "+ error.message); + } + })); + }catch(RepeatedRequestIdException e){ + System.out.println("RepeatedRequestIdException. Msg: "+e.getMessage()); + } + + + try{ + // Holding this thread while we get update notifications + synchronized (this){ + wait(); + } + }catch(InterruptedException e){ + System.out.println("InterruptedException. Msg: "+e.getMessage()); + } + } + + /** + * Test for GetRequiredFees Handler. + * + */ + @Test + public void testGetRequiredFeesRequest(){ + nodeConnection = NodeConnection.getInstance(); + nodeConnection.addNodeUrl(NODE_URL_1); + nodeConnection.connect("", "", false, mErrorListener); + + UserAccount userAccount_from = new UserAccount(ACCOUNT_ID_1); + UserAccount userAccount_to = new UserAccount(ACCOUNT_ID_2); + + //Test with 2 BTS + Asset testAsset = new Asset("1.3.0"); + AssetAmount assetAmountTest = new AssetAmount(UnsignedLong.valueOf(200000), testAsset); + AssetAmount feeAmountTest = new AssetAmount(UnsignedLong.valueOf(100000), testAsset); + + TransferOperation transferOperation = new TransferOperation(userAccount_from, userAccount_to, assetAmountTest, feeAmountTest); + + ArrayList operations = new ArrayList<>(); + operations.add(transferOperation); + + System.out.println("Adding GetBlockHeader request"); + try{ + nodeConnection.addRequestHandler(new GetRequiredFees(operations, testAsset, true, new WitnessResponseListener(){ + @Override + public void onSuccess(WitnessResponse response) { + System.out.println("GetRequiredFees.onSuccess"); + } + + @Override + public void onError(BaseResponse.Error error) { + System.out.println("GetRequiredFees.onError. Msg: "+ error.message); + } + })); + }catch(RepeatedRequestIdException e){ + System.out.println("RepeatedRequestIdException. Msg: "+e.getMessage()); + } + + + try{ + // Holding this thread while we get update notifications + synchronized (this){ + wait(); + } + }catch(InterruptedException e){ + System.out.println("InterruptedException. Msg: "+e.getMessage()); + } + } + + /** + * Test for GetTradeHistory Handler. + * + */ + @Test + public void testGetTradeHistoryRequest(){ + nodeConnection = NodeConnection.getInstance(); + nodeConnection.addNodeUrl(NODE_URL_1); + nodeConnection.connect("", "", false, mErrorListener); + + UserAccount userAccount_from = new UserAccount(ACCOUNT_ID_1); + UserAccount userAccount_to = new UserAccount(ACCOUNT_ID_2); + + + String asset_sold_id = BLOCKPAY.getBitassetId(); + String asset_purchased_id = BTS.getBitassetId(); + int limit = 10; + + System.out.println("Adding GetTradeHistory request"); + try{ + nodeConnection.addRequestHandler(new GetTradeHistory(asset_sold_id, asset_purchased_id, "", "", limit, true, new WitnessResponseListener(){ + @Override + public void onSuccess(WitnessResponse response) { + System.out.println("GetTradeHistory.onSuccess"); + } + + @Override + public void onError(BaseResponse.Error error) { + System.out.println("GetTradeHistory.onError. Msg: "+ error.message); + } + })); + }catch(RepeatedRequestIdException e){ + System.out.println("RepeatedRequestIdException. Msg: "+e.getMessage()); + } + + + try{ + // Holding this thread while we get update notifications + synchronized (this){ + wait(); + } + }catch(InterruptedException e){ + System.out.println("InterruptedException. Msg: "+e.getMessage()); + } + } + + /** + * Test for ListAsset Handler. + *' + * Request the 'list_assets' API call to the witness node + */ + @Test + public void testListAssetRequest(){ + nodeConnection = NodeConnection.getInstance(); + nodeConnection.addNodeUrl(NODE_URL_1); + nodeConnection.connect("", "", false, mErrorListener); + + UserAccount userAccount_from = new UserAccount(ACCOUNT_ID_1); + UserAccount userAccount_to = new UserAccount(ACCOUNT_ID_2); + + + String asset_symbol = BLOCKPAY.getSymbol(); + int limit = 10; + + System.out.println("Adding ListAssets request"); + try{ + nodeConnection.addRequestHandler(new ListAssets(asset_symbol, limit, true, new WitnessResponseListener(){ + @Override + public void onSuccess(WitnessResponse response) { + System.out.println("ListAssets.onSuccess"); + } + + @Override + public void onError(BaseResponse.Error error) { + System.out.println("ListAssets.onError. Msg: "+ error.message); + } + })); + }catch(RepeatedRequestIdException e){ + System.out.println("RepeatedRequestIdException. Msg: "+e.getMessage()); + } + + + try{ + // Holding this thread while we get update notifications + synchronized (this){ + wait(); + } + }catch(InterruptedException e){ + System.out.println("InterruptedException. Msg: "+e.getMessage()); + } + } + + /** + * Test for GetRelativeAccount Handler. + * + * Request for the transaction history of a user account. + */ + @Test + public void testGetLookupAccountsRequest(){ + nodeConnection = NodeConnection.getInstance(); + nodeConnection.addNodeUrl(NODE_URL_1); + nodeConnection.connect("", "", false, mErrorListener); + + UserAccount userAccount = new UserAccount(ACCOUNT_ID_1); + UserAccount userAccount_2 = new UserAccount(ACCOUNT_ID_2); + + int maxAccounts = 10; + + System.out.println("Adding LookupAccounts request"); + try{ + nodeConnection.addRequestHandler(new LookupAccounts(userAccount.getName(), true, new WitnessResponseListener(){ + @Override + public void onSuccess(WitnessResponse response) { + System.out.println("LookupAccounts.onSuccess"); + } + + @Override + public void onError(BaseResponse.Error error) { + System.out.println("LookupAccounts.onError. Msg: "+ error.message); + } + })); + }catch(RepeatedRequestIdException e){ + System.out.println("RepeatedRequestIdException. Msg: "+e.getMessage()); + } + + System.out.println("Adding LookupAccounts request . maxAccounts = "+maxAccounts); + try{ + nodeConnection.addRequestHandler(new LookupAccounts(userAccount_2.getName(), maxAccounts, true, new WitnessResponseListener(){ + @Override + public void onSuccess(WitnessResponse response) { + System.out.println("LookupAccounts.onSuccess"); + } + + @Override + public void onError(BaseResponse.Error error) { + System.out.println("LookupAccounts.onError. Msg: "+ error.message); + } + })); + }catch(RepeatedRequestIdException e){ + System.out.println("RepeatedRequestIdException. Msg: "+e.getMessage()); + } + + try{ + // Holding this thread while we get update notifications + synchronized (this){ + wait(); + } + }catch(InterruptedException e){ + System.out.println("InterruptedException. Msg: "+e.getMessage()); + } + } + + /** + * Test for LookupAssetSymbols Handler. + * + * Request for the assets corresponding to the provided symbols or IDs. + */ + @Test + public void testLookupAssetSymbolsRequest(){ + nodeConnection = NodeConnection.getInstance(); + nodeConnection.addNodeUrl(NODE_URL_1); + nodeConnection.connect("", "", false, mErrorListener); + + + ArrayList assetList = new ArrayList(){{ + add(BLOCKPAY); + add(BTS); + add(BITEURO); + }}; + + System.out.println("Adding LookupAssetSymbols request"); + try{ + nodeConnection.addRequestHandler(new LookupAssetSymbols(assetList, true, new WitnessResponseListener(){ + @Override + public void onSuccess(WitnessResponse response) { + System.out.println("LookupAssetSymbols.onSuccess"); + } + + @Override + public void onError(BaseResponse.Error error) { + System.out.println("LookupAssetSymbols.onError. Msg: "+ error.message); + } + })); + }catch(RepeatedRequestIdException e){ + System.out.println("RepeatedRequestIdException. Msg: "+e.getMessage()); + } + + try{ + // Holding this thread while we get update notifications + synchronized (this){ + wait(); + } + }catch(InterruptedException e){ + System.out.println("InterruptedException. Msg: "+e.getMessage()); + } + } + + /** + * Test for TransactionBroadcastSequence Handler. + * + */ + @Test + public void testTransactionBroadcastSequenceRequest(){ + nodeConnection = NodeConnection.getInstance(); + nodeConnection.addNodeUrl(NODE_URL_1); + nodeConnection.connect("", "", false, mErrorListener); + + + ArrayList assetList = new ArrayList(){{ + add(BLOCKPAY); + add(BTS); + add(BITEURO); + }}; + + ECKey privateKey = new BrainKey(TEST_ACCOUNT_BRAIN_KEY, 0).getPrivateKey(); + + UserAccount userAccount = new UserAccount(ACCOUNT_ID_1); + UserAccount userAccount_2 = new UserAccount(ACCOUNT_ID_2); + + TransferOperation transferOperation1 = new TransferOperationBuilder() + .setTransferAmount(new AssetAmount(UnsignedLong.valueOf(1), BTS)) + .setSource(userAccount) + .setDestination(userAccount_2) + .setFee(new AssetAmount(UnsignedLong.valueOf(264174), BTS)) + .build(); + + + ArrayList operationList = new ArrayList<>(); + operationList.add(transferOperation1); + + try{ + Transaction transaction = new Transaction(privateKey, null, operationList); + + SSLContext context = null; + context = NaiveSSLContext.getInstance("TLS"); + WebSocketFactory factory = new WebSocketFactory(); + + // Set the custom SSL context. + factory.setSSLContext(context); + + WebSocket mWebSocket = factory.createSocket(NODE_URL_1); + + mWebSocket.addListener(new TransactionBroadcastSequence(transaction, BTS, new WitnessResponseListener(){ + @Override + public void onSuccess(WitnessResponse response) { + System.out.println("TransactionBroadcastSequence.onSuccess"); + } + + @Override + public void onError(BaseResponse.Error error) { + System.out.println("TransactionBroadcastSequence.onError. Msg: "+ error.message); + } + })); + mWebSocket.connect(); + + try{ + // Holding this thread while we get update notifications + synchronized (this){ + wait(); + } + }catch(InterruptedException e){ + System.out.println("InterruptedException. Msg: "+e.getMessage()); + } + + }catch(NoSuchAlgorithmException e){ + System.out.println("NoSuchAlgoritmException. Msg: " + e.getMessage()); + } catch (IOException e) { + System.out.println("IOException. Msg: " + e.getMessage()); + } catch (WebSocketException e) { + System.out.println("WebSocketException. Msg: " + e.getMessage()); + } + } + + + private WitnessResponseListener mErrorListener = new WitnessResponseListener() { + + @Override + public void onSuccess(WitnessResponse response) { + System.out.println("onSuccess"); + } + + @Override + public void onError(BaseResponse.Error error) { + System.out.println("onError"); + } + }; +} \ No newline at end of file diff --git a/graphenej/src/test/java/de/bitsharesmunich/graphenej/objects/MemoTest.java b/graphenej/src/test/java/de/bitsharesmunich/graphenej/objects/MemoTest.java index 33cd015..cdc7101 100644 --- a/graphenej/src/test/java/de/bitsharesmunich/graphenej/objects/MemoTest.java +++ b/graphenej/src/test/java/de/bitsharesmunich/graphenej/objects/MemoTest.java @@ -3,6 +3,7 @@ package de.bitsharesmunich.graphenej.objects; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import de.bitsharesmunich.graphenej.Address; +import de.bitsharesmunich.graphenej.PublicKey; import de.bitsharesmunich.graphenej.Util; import de.bitsharesmunich.graphenej.errors.ChecksumException; import org.bitcoinj.core.DumpedPrivateKey; @@ -13,7 +14,7 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; /** - * Created by nelson on 12/19/16. + * Unit Tests for Memo Related Classes. */ public class MemoTest { @@ -24,8 +25,13 @@ public class MemoTest { private Address destinationAddress; private long nonce; + private String sourceWIF = System.getenv("SOURCE_WIF"); + private String destinationWIF = System.getenv("DESTINATION_WIF"); + private byte[] memoEncryptedEnvMessage = Util.hexToBytes(System.getenv("MEMO_MESSAGE")); + //private String sourceWIF = "5J96pne45qWM1WpektoeazN6k9Mt93jQ7LyueRxFfEMTiy6yxjM"; + //private String destinationWIF = "5HuGQT8qwHScBgD4XsGbQUmXQF18MrbzxaQDiGGXFNRrCtqgT5Q"; private String shortMessage = "test"; - private String longerMessage = "testing now longer string!!"; + private String longerMessage = "testing now longer string with some special charaters é ç o ú á í Í mMno!!"; private byte[] shortEncryptedMessage = Util.hexToBytes("4c81c2db6ebc61e3f9e0ead65c0559dd"); private byte[] longerEncryptedMessage = Util.hexToBytes("1f8a08f1ff53dcefd48eeb052d26fba425f2a917f508ce61fc3d5696b10efa17"); @@ -34,12 +40,17 @@ public class MemoTest { @Before public void setUp() throws Exception { - sourcePrivate = DumpedPrivateKey.fromBase58(null, "5J96pne45qWM1WpektoeazN6k9Mt93jQ7LyueRxFfEMTiy6yxjM").getKey(); - destinationPrivate = DumpedPrivateKey.fromBase58(null, "5HuGQT8qwHScBgD4XsGbQUmXQF18MrbzxaQDiGGXFNRrCtqgT5Q").getKey(); + //Source + sourcePrivate = DumpedPrivateKey.fromBase58(null, sourceWIF).getKey(); + PublicKey publicKey = new PublicKey(ECKey.fromPublicOnly(sourcePrivate.getPubKey())); + sourceAddress = new Address(publicKey.getKey()); - sourceAddress = new Address("BTS8RiFgs8HkcVPVobHLKEv6yL3iXcC9SWjbPVS15dDAXLG9GYhnY"); - destinationAddress = new Address("BTS8ADjGaswhfFoxMGxqCdBtzhTBJsrGadCLoc9Ey5AGc8eoVZ5bV"); + //Destination + destinationPrivate = DumpedPrivateKey.fromBase58(null, destinationWIF).getKey(); + publicKey = new PublicKey(ECKey.fromPublicOnly(destinationPrivate.getPubKey())); + destinationAddress = new Address(publicKey.getKey()); + //memo.getNonce() nonce = 5; } @@ -52,11 +63,45 @@ public class MemoTest { assertArrayEquals("Testing with longer message and nonce 1", encryptedLong, longerEncryptedMessage); } + @Test + public void shouldDecryptEnvMessage(){ + try { + String decrypted = Memo.decryptMessage(destinationPrivate, sourceAddress, nonce, memoEncryptedEnvMessage); + System.out.println("Short Decrypted Message: " + decrypted); + assertEquals("Decrypted message must be equal to original", decrypted, shortMessage); + } catch (ChecksumException e) { + e.printStackTrace(); + } + } + + @Test + public void shouldDecryptShortMessage(){ + try { + String decrypted = Memo.decryptMessage(destinationPrivate, sourceAddress, nonce, shortEncryptedMessage); + System.out.println("Short Decrypted Message: " + decrypted); + assertEquals("Decrypted message must be equal to original", decrypted, shortMessage); + } catch (ChecksumException e) { + e.printStackTrace(); + } + } + + @Test + public void shouldDecryptLongerMessage(){ + try{ + String longDecrypted = Memo.decryptMessage(destinationPrivate, sourceAddress, nonce, longerEncryptedMessage); + System.out.println("Long Decrypted Message: " + longDecrypted); + assertEquals("The longer message must be equal to the original", longerMessage, longDecrypted); + } catch (ChecksumException e) { + e.printStackTrace(); + } + } + @Test public void shouldEncryptAndDecryptShortMessage(){ try { byte[] encrypted = Memo.encryptMessage(sourcePrivate, destinationAddress, nonce, shortMessage); - String decrypted = decrypted = Memo.decryptMessage(destinationPrivate, sourceAddress, nonce, encrypted); + String decrypted = Memo.decryptMessage(destinationPrivate, sourceAddress, nonce, encrypted); + System.out.println("Short Decrypted Message: " + decrypted); assertEquals("Decrypted message must be equal to original", decrypted, shortMessage); } catch (ChecksumException e) { e.printStackTrace(); @@ -68,6 +113,7 @@ public class MemoTest { try{ byte[] longEncrypted = Memo.encryptMessage(sourcePrivate, destinationAddress, nonce, longerMessage); String longDecrypted = Memo.decryptMessage(destinationPrivate, sourceAddress, nonce, longEncrypted); + System.out.println("Long Decrypted Message: " + longDecrypted); assertEquals("The longer message must be equal to the original", longerMessage, longDecrypted); } catch (ChecksumException e) { e.printStackTrace();