Adding support for the get_relative_account_history method
This commit is contained in:
parent
e9c71b5145
commit
2b2195754a
8 changed files with 402 additions and 16 deletions
|
@ -71,6 +71,9 @@ public class Main {
|
|||
// test.testBrainKeyOperations(false);
|
||||
|
||||
// test.testBip39Opertion();
|
||||
test.testAccountNamebyAddress();
|
||||
|
||||
// test.testAccountNamebyAddress();
|
||||
|
||||
test.testRelativeAccountHistory();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,12 +4,15 @@ package com.luminiasoft.bitshares;
|
|||
* Created by nelson on 11/16/16.
|
||||
*/
|
||||
public class RPC {
|
||||
public static final String VERSION = "2.0";
|
||||
public static final String CALL_LOGIN = "login";
|
||||
public static final String CALL_NETWORK_BROADCAST = "network_broadcast";
|
||||
public static final String CALL_HISTORY = "history";
|
||||
public static final String CALL_GET_ACCOUNT_BY_NAME = "get_account_by_name";
|
||||
public static final String CALL_GET_DYNAMIC_GLOBAL_PROPERTIES = "get_dynamic_global_properties";
|
||||
public static final String CALL_BROADCAST_TRANSACTION = "broadcast_transaction";
|
||||
public static final String CALL_GET_REQUIRED_FEES = "get_required_fees";
|
||||
public static final String CALL_GET_ACCOUNTS = "get_accounts";
|
||||
public static final String CALL_GET_KEY_REFERENCES = "get_key_references";
|
||||
public static final String CALL_GET_RELATIVE_ACCOUNT_HISTORY = "get_relative_account_history";
|
||||
}
|
||||
|
|
|
@ -7,10 +7,8 @@ import com.google.gson.reflect.TypeToken;
|
|||
import com.luminiasoft.bitshares.errors.MalformedTransactionException;
|
||||
import com.luminiasoft.bitshares.interfaces.WitnessResponseListener;
|
||||
import com.luminiasoft.bitshares.models.*;
|
||||
import com.luminiasoft.bitshares.ws.GetAccountByName;
|
||||
import com.luminiasoft.bitshares.ws.GetAccountsByAddress;
|
||||
import com.luminiasoft.bitshares.ws.GetRequiredFees;
|
||||
import com.luminiasoft.bitshares.ws.TransactionBroadcastSequence;
|
||||
import com.luminiasoft.bitshares.test.NaiveSSLContext;
|
||||
import com.luminiasoft.bitshares.ws.*;
|
||||
import com.neovisionaries.ws.client.*;
|
||||
import org.bitcoinj.core.*;
|
||||
import org.spongycastle.crypto.Digest;
|
||||
|
@ -19,10 +17,14 @@ import org.spongycastle.crypto.digests.RIPEMD160Digest;
|
|||
import org.spongycastle.crypto.digests.SHA512Digest;
|
||||
import org.spongycastle.crypto.prng.DigestRandomGenerator;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLServerSocketFactory;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Type;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
|
@ -32,6 +34,9 @@ import java.util.*;
|
|||
public class Test {
|
||||
|
||||
public static final String WITNESS_URL = "ws://api.devling.xyz:8088";
|
||||
public static final String OPENLEDGER_WITNESS_URL = "wss://bitshares.openledger.info/ws";
|
||||
// public static final String WITNESS_URL = "wss://fr.blockpay.ch:8089";
|
||||
|
||||
private Transaction transaction;
|
||||
|
||||
public Transaction getTransaction() {
|
||||
|
@ -573,10 +578,10 @@ public class Test {
|
|||
BrainKey brainKey = new BrainKey(Main.BRAIN_KEY, 0);
|
||||
Address address = new Address(brainKey.getPrivateKey());
|
||||
try {
|
||||
WebSocketFactory factory = new WebSocketFactory().setConnectionTimeout(5000);
|
||||
WebSocket mWebSocket = factory.createSocket(WITNESS_URL);
|
||||
WebSocket mWebSocket = new WebSocketFactory().createSocket(WITNESS_URL);
|
||||
byte[] key = brainKey.getPrivateKey().getPubKey();
|
||||
mWebSocket.addListener(new GetAccountsByAddress(key, mListener));
|
||||
System.out.println("Before connecting");
|
||||
mWebSocket.connect();
|
||||
} catch (IOException e) {
|
||||
System.out.println("IOException. Msg: " + e.getMessage());
|
||||
|
@ -584,4 +589,27 @@ public class Test {
|
|||
System.out.println("WebSocketException. Msg: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void testRelativeAccountHistory(){
|
||||
GetRelativeAccountHistory relativeAccountHistory = new GetRelativeAccountHistory(new UserAccount("1.2.138632"), mListener);
|
||||
try {
|
||||
// Create a custom SSL context.
|
||||
SSLContext context = null;
|
||||
context = NaiveSSLContext.getInstance("TLS");
|
||||
WebSocketFactory factory = new WebSocketFactory();
|
||||
|
||||
// Set the custom SSL context.
|
||||
factory.setSSLContext(context);
|
||||
|
||||
WebSocket mWebSocket = factory.createSocket(OPENLEDGER_WITNESS_URL);
|
||||
mWebSocket.addListener(relativeAccountHistory);
|
||||
mWebSocket.connect();
|
||||
} catch (IOException e) {
|
||||
System.out.println("IOException. Msg: "+e.getMessage());
|
||||
} catch (WebSocketException e) {
|
||||
System.out.println("WebSocketException. Msg: "+e.getMessage());
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
System.out.println("NoSuchAlgorithmException. Msg: "+e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,14 @@
|
|||
package com.luminiasoft.bitshares;
|
||||
|
||||
import com.google.common.primitives.Bytes;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import com.google.common.primitives.UnsignedLong;
|
||||
import com.google.gson.*;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* Class used to encapsulate the Transfer operation related functionalities.
|
||||
* TODO: Add extensions support
|
||||
*/
|
||||
public class Transfer extends BaseOperation {
|
||||
public static final String KEY_FEE = "fee";
|
||||
|
@ -100,14 +97,72 @@ public class Transfer extends BaseOperation {
|
|||
return array;
|
||||
}
|
||||
|
||||
class TransferSerializer implements JsonSerializer<Transfer> {
|
||||
public static class TransferSerializer implements JsonSerializer<Transfer> {
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(Transfer transfer, Type type, JsonSerializationContext jsonSerializationContext) {
|
||||
JsonArray arrayRep = new JsonArray();
|
||||
arrayRep.add(transfer.getId());
|
||||
arrayRep.add(toJsonObject());
|
||||
arrayRep.add(transfer.toJsonObject());
|
||||
return arrayRep;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This deserializer will work on any transfer operation serialized in the 'array form' used a lot in
|
||||
* the Graphene Blockchain API.
|
||||
*
|
||||
* An example of this serialized form is the following:
|
||||
*
|
||||
* [
|
||||
* 0,
|
||||
* {
|
||||
* "fee": {
|
||||
* "amount": 264174,
|
||||
* "asset_id": "1.3.0"
|
||||
* },
|
||||
* "from": "1.2.138632",
|
||||
* "to": "1.2.129848",
|
||||
* "amount": {
|
||||
* "amount": 100,
|
||||
* "asset_id": "1.3.0"
|
||||
* },
|
||||
* "extensions": []
|
||||
* }
|
||||
* ]
|
||||
*
|
||||
* It will convert this data into a nice Transfer object.
|
||||
*/
|
||||
public static class TransferDeserializer implements JsonDeserializer<Transfer> {
|
||||
|
||||
@Override
|
||||
public Transfer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||
if(json.isJsonArray()){
|
||||
// This block is used just to check if we are in the first step of the deserialization
|
||||
// when we are dealing with an array.
|
||||
JsonArray serializedTransfer = json.getAsJsonArray();
|
||||
if(serializedTransfer.get(0).getAsInt() != OperationType.transfer_operation.ordinal()){
|
||||
// If the operation type does not correspond to a transfer operation, we return null
|
||||
return null;
|
||||
}else{
|
||||
// Calling itself recursively, this is only done once, so there will be no problems.
|
||||
return context.deserialize(serializedTransfer.get(1), Transfer.class);
|
||||
}
|
||||
}else{
|
||||
// This block is called in the second recursion and takes care of deserializing the
|
||||
// transfer data itself.
|
||||
JsonObject jsonObject = json.getAsJsonObject();
|
||||
|
||||
// Deserializing AssetAmount objects
|
||||
AssetAmount amount = context.deserialize(jsonObject.get("amount"), AssetAmount.class);
|
||||
AssetAmount fee = context.deserialize(jsonObject.get("fee"), AssetAmount.class);
|
||||
|
||||
// Deserializing UserAccount objects
|
||||
UserAccount from = new UserAccount(jsonObject.get("from").getAsString());
|
||||
UserAccount to = new UserAccount(jsonObject.get("to").getAsString());
|
||||
Transfer transfer = new Transfer(from, to, amount, fee);
|
||||
return transfer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,6 +69,9 @@ public class ApiCall implements JsonSerializable {
|
|||
if(this.params.get(i) instanceof JsonSerializable) {
|
||||
// Sometimes the parameters are objects
|
||||
methodParams.add(((JsonSerializable) this.params.get(i)).toJsonObject());
|
||||
}else if (Number.class.isInstance(this.params.get(i))){
|
||||
// Other times they are numbers
|
||||
methodParams.add( (Number) this.params.get(i));
|
||||
}else if(this.params.get(i) instanceof String || this.params.get(i) == null){
|
||||
// Other times they are plain strings
|
||||
methodParams.add((String) this.params.get(i));
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
package com.luminiasoft.bitshares.models;
|
||||
|
||||
import com.luminiasoft.bitshares.BaseOperation;
|
||||
import com.luminiasoft.bitshares.GrapheneObject;
|
||||
import com.luminiasoft.bitshares.Transfer;
|
||||
|
||||
/**
|
||||
* This class offers support to deserialization of transfer operations received by the API
|
||||
* method get_relative_account_history.
|
||||
*
|
||||
* More operations types might be listed in the response of that method, but by using this class
|
||||
* those will be filtered out of the parsed result.
|
||||
*/
|
||||
public class HistoricalTransfer {
|
||||
public String id;
|
||||
public Transfer op;
|
||||
public Object[] result;
|
||||
public long block_num;
|
||||
public long trx_in_block;
|
||||
public long op_in_trx;
|
||||
public long virtual_op;
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
package com.luminiasoft.bitshares.test;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 Neo Visionaries Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.Provider;
|
||||
import java.security.cert.X509Certificate;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
|
||||
/**
|
||||
* A factory class which creates an {@link SSLContext} that
|
||||
* naively accepts all certificates without verification.
|
||||
*
|
||||
* <pre>
|
||||
* // Create an SSL context that naively accepts all certificates.
|
||||
* SSLContext context = NaiveSSLContext.getInstance("TLS");
|
||||
*
|
||||
* // Create a socket factory from the SSL context.
|
||||
* SSLSocketFactory factory = context.getSocketFactory();
|
||||
*
|
||||
* // Create a socket from the socket factory.
|
||||
* SSLSocket socket = factory.createSocket("www.example.com", 443);
|
||||
* </pre>
|
||||
*
|
||||
* @author Takahiko Kawasaki
|
||||
*/
|
||||
public class NaiveSSLContext
|
||||
{
|
||||
private NaiveSSLContext()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an SSLContext that implements the specified secure
|
||||
* socket protocol and naively accepts all certificates
|
||||
* without verification.
|
||||
*/
|
||||
public static SSLContext getInstance(String protocol) throws NoSuchAlgorithmException
|
||||
{
|
||||
return init(SSLContext.getInstance(protocol));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an SSLContext that implements the specified secure
|
||||
* socket protocol and naively accepts all certificates
|
||||
* without verification.
|
||||
*/
|
||||
public static SSLContext getInstance(String protocol, Provider provider) throws NoSuchAlgorithmException
|
||||
{
|
||||
return init(SSLContext.getInstance(protocol, provider));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an SSLContext that implements the specified secure
|
||||
* socket protocol and naively accepts all certificates
|
||||
* without verification.
|
||||
*/
|
||||
public static SSLContext getInstance(String protocol, String provider) throws NoSuchAlgorithmException, NoSuchProviderException
|
||||
{
|
||||
return init(SSLContext.getInstance(protocol, provider));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set NaiveTrustManager to the given context.
|
||||
*/
|
||||
private static SSLContext init(SSLContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Set NaiveTrustManager.
|
||||
context.init(null, new TrustManager[] { new NaiveTrustManager() }, null);
|
||||
}
|
||||
catch (KeyManagementException e)
|
||||
{
|
||||
throw new RuntimeException("Failed to initialize an SSLContext.", e);
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A {@link TrustManager} which trusts all certificates naively.
|
||||
*/
|
||||
private static class NaiveTrustManager implements X509TrustManager
|
||||
{
|
||||
@Override
|
||||
public X509Certificate[] getAcceptedIssuers()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public void checkClientTrusted(X509Certificate[] certs, String authType)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public void checkServerTrusted(X509Certificate[] certs, String authType)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
package com.luminiasoft.bitshares.ws;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.luminiasoft.bitshares.AssetAmount;
|
||||
import com.luminiasoft.bitshares.RPC;
|
||||
import com.luminiasoft.bitshares.Transfer;
|
||||
import com.luminiasoft.bitshares.UserAccount;
|
||||
import com.luminiasoft.bitshares.interfaces.WitnessResponseListener;
|
||||
import com.luminiasoft.bitshares.models.ApiCall;
|
||||
import com.luminiasoft.bitshares.models.BaseResponse;
|
||||
import com.luminiasoft.bitshares.models.HistoricalTransfer;
|
||||
import com.luminiasoft.bitshares.models.WitnessResponse;
|
||||
import com.neovisionaries.ws.client.WebSocket;
|
||||
import com.neovisionaries.ws.client.WebSocketAdapter;
|
||||
import com.neovisionaries.ws.client.WebSocketException;
|
||||
import com.neovisionaries.ws.client.WebSocketFrame;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Class used to encapsulate the communication sequence used to retrieve the transaction history of
|
||||
* a given user.
|
||||
*/
|
||||
public class GetRelativeAccountHistory extends WebSocketAdapter {
|
||||
// Sequence of message ids
|
||||
private final static int LOGIN_ID = 1;
|
||||
private final static int GET_HISTORY_ID = 2;
|
||||
private final static int GET_HISTORY_DATA = 3;
|
||||
|
||||
// Default value constants
|
||||
public static final int DEFAULT_STOP = 0;
|
||||
public static final int DEFAULT_START = 0;
|
||||
public static final int MAX_LIMIT = 100;
|
||||
|
||||
// API call parameters
|
||||
private UserAccount mUserAccount;
|
||||
private int stop;
|
||||
private int limit;
|
||||
private int start;
|
||||
private WitnessResponseListener mListener;
|
||||
|
||||
private int currentId = 1;
|
||||
private int apiId = -1;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public GetRelativeAccountHistory(UserAccount userAccount, int stop, int limit, int start, WitnessResponseListener listener){
|
||||
if(limit > MAX_LIMIT) limit = MAX_LIMIT;
|
||||
this.mUserAccount = userAccount;
|
||||
this.stop = stop;
|
||||
this.limit = limit;
|
||||
this.start = start;
|
||||
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
|
||||
*/
|
||||
public GetRelativeAccountHistory(UserAccount userAccount, WitnessResponseListener listener){
|
||||
this.mUserAccount = userAccount;
|
||||
this.stop = DEFAULT_STOP;
|
||||
this.limit = MAX_LIMIT;
|
||||
this.start = DEFAULT_START;
|
||||
this.mListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnected(WebSocket websocket, Map<String, List<String>> headers) throws Exception {
|
||||
ArrayList<Serializable> loginParams = new ArrayList<>();
|
||||
loginParams.add(null);
|
||||
loginParams.add(null);
|
||||
ApiCall loginCall = new ApiCall(1, RPC.CALL_LOGIN, loginParams, "2.0", currentId);
|
||||
websocket.sendText(loginCall.toJsonString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
|
||||
String response = frame.getPayloadText();
|
||||
System.out.println("<<< "+response);
|
||||
Gson gson = new Gson();
|
||||
BaseResponse baseResponse = gson.fromJson(response, BaseResponse.class);
|
||||
if(baseResponse.error != null){
|
||||
mListener.onError(baseResponse.error);
|
||||
websocket.disconnect();
|
||||
}else{
|
||||
currentId++;
|
||||
ArrayList<Serializable> emptyParams = new ArrayList<>();
|
||||
if(baseResponse.id == LOGIN_ID){
|
||||
ApiCall getRelativeAccountHistoryId = new ApiCall(1, RPC.CALL_HISTORY, emptyParams, RPC.VERSION, currentId);
|
||||
websocket.sendText(getRelativeAccountHistoryId.toJsonString());
|
||||
}else if(baseResponse.id == GET_HISTORY_ID){
|
||||
Type ApiIdResponse = new TypeToken<WitnessResponse<Integer>>() {}.getType();
|
||||
WitnessResponse<Integer> witnessResponse = gson.fromJson(response, ApiIdResponse);
|
||||
apiId = witnessResponse.result.intValue();
|
||||
|
||||
ArrayList<Serializable> params = new ArrayList<>();
|
||||
params.add(mUserAccount.toJsonString());
|
||||
params.add(this.stop);
|
||||
params.add(this.limit);
|
||||
params.add(this.start);
|
||||
|
||||
ApiCall getRelativeAccountHistoryCall = new ApiCall(apiId, RPC.CALL_GET_RELATIVE_ACCOUNT_HISTORY, params, RPC.VERSION, currentId);
|
||||
websocket.sendText(getRelativeAccountHistoryCall.toJsonString());
|
||||
}else if(baseResponse.id == GET_HISTORY_DATA){
|
||||
System.out.println(frame.getPayloadText());
|
||||
Type RelativeAccountHistoryResponse = new TypeToken<WitnessResponse<List<HistoricalTransfer>>>(){}.getType();
|
||||
GsonBuilder gsonBuilder = new GsonBuilder();
|
||||
gsonBuilder.registerTypeAdapter(Transfer.class, new Transfer.TransferDeserializer());
|
||||
gsonBuilder.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetDeserializer());
|
||||
WitnessResponse<List<HistoricalTransfer>> transfersResponse = gsonBuilder.create().fromJson(response, RelativeAccountHistoryResponse);
|
||||
mListener.onSuccess(transfersResponse);
|
||||
websocket.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFrameSent(WebSocket websocket, WebSocketFrame frame) throws Exception {
|
||||
if(frame.isTextFrame())
|
||||
System.out.println(">>> "+frame.getPayloadText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(WebSocket websocket, WebSocketException cause) throws Exception {
|
||||
System.out.println("onError. Msg: "+cause.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCallbackError(WebSocket websocket, Throwable cause) throws Exception {
|
||||
System.out.println("handleCallbackError. Msg: "+cause.getMessage());
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue