diff --git a/graphenej/src/main/java/cy/agorise/graphenej/RPC.java b/graphenej/src/main/java/cy/agorise/graphenej/RPC.java index e1f42c0..ea6c509 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/RPC.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/RPC.java @@ -25,6 +25,7 @@ public class RPC { public static final String GET_ACCOUNT_BALANCES = "get_account_balances"; public static final String CALL_LOOKUP_ASSET_SYMBOLS = "lookup_asset_symbols"; public static final String CALL_GET_BLOCK_HEADER = "get_block_header"; + public static final String CALL_GET_BLOCK = "get_block"; public static final String CALL_GET_LIMIT_ORDERS = "get_limit_orders"; public static final String CALL_GET_TRADE_HISTORY = "get_trade_history"; public static final String CALL_GET_MARKET_HISTORY = "get_market_history"; diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/GetBlock.java b/graphenej/src/main/java/cy/agorise/graphenej/api/GetBlock.java new file mode 100644 index 0000000..2cef872 --- /dev/null +++ b/graphenej/src/main/java/cy/agorise/graphenej/api/GetBlock.java @@ -0,0 +1,108 @@ +package cy.agorise.graphenej.api; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import com.neovisionaries.ws.client.WebSocket; +import com.neovisionaries.ws.client.WebSocketFrame; +import cy.agorise.graphenej.AssetAmount; +import cy.agorise.graphenej.RPC; +import cy.agorise.graphenej.Transaction; +import cy.agorise.graphenej.interfaces.WitnessResponseListener; +import cy.agorise.graphenej.models.ApiCall; +import cy.agorise.graphenej.models.BaseResponse; +import cy.agorise.graphenej.models.Block; +import cy.agorise.graphenej.models.WitnessResponse; +import cy.agorise.graphenej.operations.TransferOperation; + +import java.io.Serializable; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class GetBlock extends BaseGrapheneHandler { + + private final static int LOGIN_ID = 1; + private final static int GET_DATABASE_ID = 2; + private final static int GET_BLOCK_ID = 3; + + private long blockNumber; + private WitnessResponseListener mListener; + + private int currentId = LOGIN_ID; + + private boolean mOneTime; + + public GetBlock(long blockNumber, boolean oneTime, WitnessResponseListener listener){ + super(listener); + this.blockNumber = blockNumber; + this.mOneTime = oneTime; + this.mListener = listener; + } + + public GetBlock(long blockNumber, WitnessResponseListener listener){ + this(blockNumber, true, listener); + } + + @Override + public void onConnected(WebSocket websocket, Map> headers) throws Exception { + ArrayList loginParams = new ArrayList<>(); + loginParams.add(null); + loginParams.add(null); + ApiCall loginCall = new ApiCall(1, RPC.CALL_LOGIN, loginParams, RPC.VERSION, 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); + if(mOneTime){ + websocket.disconnect(); + } + } else { + currentId++; + ArrayList emptyParams = new ArrayList<>(); + if (baseResponse.id == LOGIN_ID) { + ApiCall getDatabaseId = new ApiCall(1, RPC.CALL_DATABASE, emptyParams, RPC.VERSION, currentId); + websocket.sendText(getDatabaseId.toJsonString()); + } else if (baseResponse.id == GET_DATABASE_ID) { + Type ApiIdResponse = new TypeToken>() {}.getType(); + WitnessResponse witnessResponse = gson.fromJson(response, ApiIdResponse); + Integer apiId = witnessResponse.result; + + ArrayList params = new ArrayList<>(); + String blockNum = String.format("%d", this.blockNumber); + params.add(blockNum); + + ApiCall loginCall = new ApiCall(apiId, RPC.CALL_GET_BLOCK, params, RPC.VERSION, currentId); + websocket.sendText(loginCall.toJsonString()); + } else if (baseResponse.id == GET_BLOCK_ID) { + Type BlockResponse = new TypeToken>(){}.getType(); + gson = new GsonBuilder() + .registerTypeAdapter(Transaction.class, new Transaction.TransactionDeserializer()) + .registerTypeAdapter(TransferOperation.class, new TransferOperation.TransferDeserializer()) + .registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer()) + .create(); + WitnessResponse blockResponse = gson.fromJson(response, BlockResponse); + mListener.onSuccess(blockResponse); + if (mOneTime) { + websocket.disconnect(); + } + } + } + + } + + @Override + public void onFrameSent(WebSocket websocket, WebSocketFrame frame) throws Exception { + if(frame.isTextFrame()) + System.out.println(">>> "+frame.getPayloadText()); + } +} diff --git a/graphenej/src/main/java/cy/agorise/graphenej/models/Block.java b/graphenej/src/main/java/cy/agorise/graphenej/models/Block.java new file mode 100644 index 0000000..625d9d4 --- /dev/null +++ b/graphenej/src/main/java/cy/agorise/graphenej/models/Block.java @@ -0,0 +1,71 @@ +package cy.agorise.graphenej.models; + +import cy.agorise.graphenej.Transaction; + +import java.util.List; + +public class Block { + private String previous; + private String timestamp; + private String witness; + private String transaction_merkle_root; + private Object[] extensions; + private String witness_signature; + private List transactions; + + public String getPrevious() { + return previous; + } + + public void setPrevious(String previous) { + this.previous = previous; + } + + public String getTimestamp() { + return timestamp; + } + + public void setTimestamp(String timestamp) { + this.timestamp = timestamp; + } + + public String getWitness() { + return witness; + } + + public void setWitness(String witness) { + this.witness = witness; + } + + public String getTransaction_merkle_root() { + return transaction_merkle_root; + } + + public void setTransaction_merkle_root(String transaction_merkle_root) { + this.transaction_merkle_root = transaction_merkle_root; + } + + public Object[] getExtensions() { + return extensions; + } + + public void setExtensions(Object[] extensions) { + this.extensions = extensions; + } + + public String getWitness_signature() { + return witness_signature; + } + + public void setWitness_signature(String witness_signature) { + this.witness_signature = witness_signature; + } + + public List getTransactions() { + return transactions; + } + + public void setTransactions(List transactions) { + this.transactions = transactions; + } +} diff --git a/graphenej/src/test/java/cy/agorise/graphenej/api/GetBlockTest.java b/graphenej/src/test/java/cy/agorise/graphenej/api/GetBlockTest.java new file mode 100644 index 0000000..1689e04 --- /dev/null +++ b/graphenej/src/test/java/cy/agorise/graphenej/api/GetBlockTest.java @@ -0,0 +1,110 @@ +package cy.agorise.graphenej.api; + +import com.google.common.primitives.UnsignedLong; +import com.neovisionaries.ws.client.WebSocketException; +import cy.agorise.graphenej.AssetAmount; +import cy.agorise.graphenej.BaseOperation; +import cy.agorise.graphenej.Transaction; +import cy.agorise.graphenej.interfaces.WitnessResponseListener; +import cy.agorise.graphenej.models.BaseResponse; +import cy.agorise.graphenej.models.Block; +import cy.agorise.graphenej.models.WitnessResponse; +import cy.agorise.graphenej.operations.TransferOperation; +import org.junit.Assert; +import org.junit.Test; + +import java.util.List; +import java.util.concurrent.Exchanger; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.hamcrest.CoreMatchers.instanceOf; + +public class GetBlockTest extends BaseApiTest { + private final static long BLOCK_NUMBER = 15776988L; + + private final static String EXPECTED_PREVIOUS = "00f0bcdbe3d9dc66d8597e7b013a16d8dea5f778"; + private final static String EXPECTED_TIMESTAMP = "2017-04-18T04:14:51"; + private final static String EXPECTED_WITNESS = "1.6.17"; + private final static String EXPECTED_TRANSACTION_MERKLE_ROOT = "fb87a3f8af907a2450c327d181b2833b8270a504"; + private final static int EXPECTED_EXTENSIONS_SIZE = 0; + private final static String EXPECTED_WITNESS_SIGNATURE = "20160921c8ba0d312dee14bdc780c3d05b27e406e2d014b8a2415e9843bf7075cb7abbc45f5173ffefac69cecf4dd2afa5dce6076bdf24cc577ff49427babe75e1"; + private final static int EXPECTED_TRANSACTIONS_SIZE = 1; + private final static int EXPECTED_OPERATIONS_SIZE = 1; + + private final static UnsignedLong EXPECTED_FEE_AMOUNT = UnsignedLong.valueOf(2048); + private final static String EXPECTED_FEE_ASSET_ID = "1.3.113"; + private final static String EXPECTED_FROM = "1.2.151069"; + private final static String EXPECTED_TO = "1.2.116354"; + private final static UnsignedLong EXPECTED_AMOUNT = UnsignedLong.valueOf(13700); + private final static String EXPECTED_ASSET_ID = "1.3.113"; + + @Test + public void testGetBlock() { + try { + final Exchanger responseExchanger = new Exchanger<>(); + mWebSocket.addListener(new GetBlock(BLOCK_NUMBER, new WitnessResponseListener() { + @Override + public void onSuccess(WitnessResponse response) { + System.out.println("onSuccess"); + try { + responseExchanger.exchange(response.result); + } catch (InterruptedException e) { + System.out.println("InterruptedException in success handler. Msg: " + e.getMessage()); + } + } + + @Override + public void onError(BaseResponse.Error error) { + System.out.println("onError"); + try { + responseExchanger.exchange(null); + } catch (InterruptedException e) { + System.out.println("InterruptedException in error handler. Msg: " + e.getMessage()); + } + } + })); + + mWebSocket.connect(); + + Object responseResult = responseExchanger.exchange(null, 5, TimeUnit.SECONDS); + + Block block = (Block) responseResult; + Assert.assertNotNull(block); + Assert.assertEquals(EXPECTED_PREVIOUS, block.getPrevious()); + Assert.assertEquals(EXPECTED_TIMESTAMP, block.getTimestamp()); + Assert.assertEquals(EXPECTED_WITNESS, block.getWitness()); + Assert.assertEquals(EXPECTED_TRANSACTION_MERKLE_ROOT, block.getTransaction_merkle_root()); + Assert.assertEquals(EXPECTED_EXTENSIONS_SIZE, block.getExtensions().length); + Assert.assertEquals(EXPECTED_WITNESS_SIGNATURE, block.getWitness_signature()); + + List transactions = block.getTransactions(); + Assert.assertEquals(EXPECTED_TRANSACTIONS_SIZE, transactions.size()); + + List operations = transactions.get(0).getOperations(); + Assert.assertEquals(EXPECTED_OPERATIONS_SIZE, operations.size()); + + BaseOperation operation = operations.get(0); + Assert.assertThat(operation, instanceOf(TransferOperation.class)); + + TransferOperation transferOperation = (TransferOperation) operation; + AssetAmount fee = transferOperation.getFee(); + Assert.assertEquals(EXPECTED_FEE_AMOUNT, fee.getAmount()); + Assert.assertEquals(EXPECTED_FEE_ASSET_ID, fee.getAsset().getObjectId()); + Assert.assertEquals(EXPECTED_FROM, transferOperation.getFrom().getObjectId()); + Assert.assertEquals(EXPECTED_TO, transferOperation.getTo().getObjectId()); + AssetAmount assetAmount = transferOperation.getAssetAmount(); + Assert.assertEquals(EXPECTED_AMOUNT, assetAmount.getAmount()); + Assert.assertEquals(EXPECTED_ASSET_ID, assetAmount.getAsset().getObjectId()); + } catch (WebSocketException e) { + System.out.println("WebSocketException. Msg: " + e.getMessage()); + Assert.fail("Fail because of WebSocketException"); + } catch (InterruptedException e) { + System.out.println("InterruptedException. Msg: " + e.getMessage()); + Assert.fail("Fail because of InterruptedException"); + } catch (TimeoutException e) { + System.out.println("TimeoutException. Msg: " + e.getMessage()); + Assert.fail("Fail because of TimeoutException"); + } + } +}