From 5ee4ef77f206939cff67496f4de77a39de6f5f32 Mon Sep 17 00:00:00 2001 From: "Nelson R. Perez" Date: Mon, 6 Mar 2017 17:04:20 -0500 Subject: [PATCH 1/7] Added limit_order_create_operation and implemented its toBytes and toJsonObject serialization, and a test class --- .../de/bitsharesmunich/graphenej/Main.java | 4 +- .../de/bitsharesmunich/graphenej/Test.java | 2 +- .../graphenej/BaseOperation.java | 3 + .../graphenej/TransactionBuilder.java | 26 ------ .../operations/LimitOrderCreateOperation.java | 86 +++++++++++++++++++ .../operations/TransferOperation.java | 2 - .../LimitOrderCreateOperationTest.java | 56 ++++++++++++ 7 files changed, 148 insertions(+), 31 deletions(-) delete mode 100644 graphenej/src/main/java/de/bitsharesmunich/graphenej/TransactionBuilder.java create mode 100644 graphenej/src/main/java/de/bitsharesmunich/graphenej/operations/LimitOrderCreateOperation.java create mode 100644 graphenej/src/test/java/de/bitsharesmunich/graphenej/operations/LimitOrderCreateOperationTest.java diff --git a/app/src/main/java/de/bitsharesmunich/graphenej/Main.java b/app/src/main/java/de/bitsharesmunich/graphenej/Main.java index 75b710f..8fcabf3 100644 --- a/app/src/main/java/de/bitsharesmunich/graphenej/Main.java +++ b/app/src/main/java/de/bitsharesmunich/graphenej/Main.java @@ -50,7 +50,7 @@ public class Main { // e.printStackTrace(); // } // test.testCustomSerializer(); -// test.testUserAccountSerialization(); + test.testUserAccountSerialization(); // test.testTransactionSerialization(); // test.testLoginSerialization(); // test.testNetworkBroadcastSerialization(); @@ -76,7 +76,7 @@ public class Main { // test.testAccountUpdateOperationBroadcast(); // test.testCreateBinFile(); // test.testImportBinFile(); - test.testExportBinFile(); +// test.testExportBinFile(); // test.testLzmaCompression(); // test.testLzmaDecompression(); // test.testSimpleDecompression(); diff --git a/app/src/main/java/de/bitsharesmunich/graphenej/Test.java b/app/src/main/java/de/bitsharesmunich/graphenej/Test.java index 68ffad5..42306e2 100644 --- a/app/src/main/java/de/bitsharesmunich/graphenej/Test.java +++ b/app/src/main/java/de/bitsharesmunich/graphenej/Test.java @@ -221,7 +221,7 @@ public class Test { } public void testUserAccountSerialization() { - UserAccount account = new UserAccount("1.2.138632"); + UserAccount account = new UserAccount("1.2.143563"); System.out.println(Util.bytesToHex(account.toBytes())); } diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/BaseOperation.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/BaseOperation.java index caf1721..36bdf10 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/BaseOperation.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/BaseOperation.java @@ -8,6 +8,9 @@ import de.bitsharesmunich.graphenej.interfaces.JsonSerializable; */ public abstract class BaseOperation implements ByteSerializable, JsonSerializable { + public static final String KEY_FEE = "fee"; + public static final String KEY_EXTENSIONS = "extensions"; + protected OperationType type; protected Extensions extensions; diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/TransactionBuilder.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/TransactionBuilder.java deleted file mode 100644 index fecf99e..0000000 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/TransactionBuilder.java +++ /dev/null @@ -1,26 +0,0 @@ -package de.bitsharesmunich.graphenej; - -import de.bitsharesmunich.graphenej.errors.MalformedTransactionException; -import org.bitcoinj.core.ECKey; - - -/** - * Created by nelson on 11/14/16. - */ -public abstract class TransactionBuilder { - protected ECKey privateKey; - protected BlockData blockData; - - public TransactionBuilder(){} - - public TransactionBuilder(ECKey privKey){ - this.privateKey = privKey; - } - - public TransactionBuilder setBlockData(BlockData blockData){ - this.blockData = blockData; - return this; - } - - public abstract Transaction build() throws MalformedTransactionException; -} diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/operations/LimitOrderCreateOperation.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/operations/LimitOrderCreateOperation.java new file mode 100644 index 0000000..a522542 --- /dev/null +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/operations/LimitOrderCreateOperation.java @@ -0,0 +1,86 @@ +package de.bitsharesmunich.graphenej.operations; + +import com.google.common.primitives.Bytes; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import de.bitsharesmunich.graphenej.*; + +import java.nio.ByteBuffer; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * Operation used to denote the creation of a limit order on the blockchain. + */ +public class LimitOrderCreateOperation extends BaseOperation { + // Number of bytes used for the expiration field. + private final int EXPIRATION_BYTE_LENGTH = 4; + + // Constants used in the JSON representation + public static final String KEY_SELLER = "seller"; + public static final String KEY_AMOUNT_TO_SELL = "amount_to_sell"; + public static final String KEY_MIN_TO_RECEIVE = "min_to_receive"; + public static final String KEY_EXPIRATION = "expiration"; + public static final String KEY_FILL_OR_KILL = "fill_or_kill"; + + // Inner fields of a limit order + private AssetAmount fee; + private UserAccount seller; + private AssetAmount toSell; + private AssetAmount minToReceive; + private int expiration; + private boolean fillOrKill; + + public LimitOrderCreateOperation(UserAccount seller, AssetAmount toSell, AssetAmount minToReceive, int expiration, boolean fillOrKill){ + super(OperationType.LIMIT_ORDER_CREATE_OPERATION); + this.seller = seller; + this.toSell = toSell; + this.minToReceive = minToReceive; + this.expiration = expiration; + this.fillOrKill = fillOrKill; + } + + @Override + public String toJsonString() { + return null; + } + + @Override + public JsonElement toJsonObject() { + JsonArray array = new JsonArray(); + array.add(this.getId()); + JsonObject jsonObject = new JsonObject(); + if(fee != null) + jsonObject.add(KEY_FEE, fee.toJsonObject()); + jsonObject.addProperty(KEY_SELLER, seller.toJsonString()); + jsonObject.add(KEY_AMOUNT_TO_SELL, toSell.toJsonObject()); + jsonObject.add(KEY_MIN_TO_RECEIVE, minToReceive.toJsonObject()); + + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(Util.TIME_DATE_FORMAT); + jsonObject.addProperty(KEY_EXPIRATION, simpleDateFormat.format(new Date(expiration))); + jsonObject.add(KEY_EXTENSIONS, new JsonArray()); + array.add(jsonObject); + return array; + } + + @Override + public void setFee(AssetAmount assetAmount) { + this.fee = assetAmount; + } + + @Override + public byte[] toBytes() { + byte[] feeBytes = this.fee.toBytes(); + byte[] sellerBytes = this.seller.toBytes(); + byte[] amountBytes = this.toSell.toBytes(); + byte[] minAmountBytes = this.minToReceive.toBytes(); + + ByteBuffer buffer = ByteBuffer.allocate(EXPIRATION_BYTE_LENGTH); + buffer.putInt(this.expiration); + byte[] expirationBytes = Util.revertBytes(buffer.array()); + + byte[] fillOrKill = this.fillOrKill ? new byte[]{ 0x1 } : new byte[]{ 0x0 }; + return Bytes.concat(feeBytes, sellerBytes, amountBytes, minAmountBytes, expirationBytes, fillOrKill); + } +} 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 73ddbc9..882c584 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/operations/TransferOperation.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/operations/TransferOperation.java @@ -14,9 +14,7 @@ import java.lang.reflect.Type; * Class used to encapsulate the TransferOperation operation related functionalities. */ public class TransferOperation extends BaseOperation { - public static final String KEY_FEE = "fee"; public static final String KEY_AMOUNT = "amount"; - public static final String KEY_EXTENSIONS = "extensions"; public static final String KEY_FROM = "from"; public static final String KEY_TO = "to"; public static final String KEY_MEMO = "memo"; diff --git a/graphenej/src/test/java/de/bitsharesmunich/graphenej/operations/LimitOrderCreateOperationTest.java b/graphenej/src/test/java/de/bitsharesmunich/graphenej/operations/LimitOrderCreateOperationTest.java new file mode 100644 index 0000000..52fc9cb --- /dev/null +++ b/graphenej/src/test/java/de/bitsharesmunich/graphenej/operations/LimitOrderCreateOperationTest.java @@ -0,0 +1,56 @@ +package de.bitsharesmunich.graphenej.operations; + +import com.google.common.primitives.UnsignedLong; +import de.bitsharesmunich.graphenej.Asset; +import de.bitsharesmunich.graphenej.AssetAmount; +import de.bitsharesmunich.graphenej.UserAccount; +import de.bitsharesmunich.graphenej.Util; +import org.hamcrest.core.IsEqual; +import org.hamcrest.core.IsNot; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Created by nelson on 3/6/17. + */ +public class LimitOrderCreateOperationTest { + private final int AMOUNT_TO_SELL = 25000000; + private final int MIN_TO_RECEIVE = 1; + private final Asset CORE_ASSET = new Asset("1.3.0"); + private final Asset BIT_USD = new Asset("1.3.121"); + private final int DEFAULT_EXPIRATION = 1488831620; // 2017-03-06T20:20:20 + + private UserAccount seller; + private AssetAmount amountToSell; + private AssetAmount minToReceive; + private int expiration; + private boolean fillOrKill; + + @Before + public void setup(){ + seller = new UserAccount("1.2.143563"); + amountToSell = new AssetAmount(UnsignedLong.valueOf(AMOUNT_TO_SELL), CORE_ASSET); + minToReceive = new AssetAmount(UnsignedLong.valueOf(MIN_TO_RECEIVE), BIT_USD); + expiration = DEFAULT_EXPIRATION; + } + + @Test + public void toBytes() throws Exception { + // Testing serialization of operation with fillOrKill parameter == true + LimitOrderCreateOperation operation = new LimitOrderCreateOperation(seller, amountToSell, minToReceive, expiration, true); + operation.setFee(new AssetAmount(UnsignedLong.valueOf(2), CORE_ASSET)); + byte[] serialized = operation.toBytes(); + Assert.assertArrayEquals("Correct serialization", serialized, Util.hexToBytes("020000000000000000cbe10840787d01000000000001000000000000007984c4bd5801")); + Assert.assertThat("Incorrect serialization", serialized, IsNot.not(IsEqual.equalTo("020000000000000000cbe10840787d01000000000001000000000000007984c4bd5800"))); + + // Testing serialization of operation with fillOrKill parameter == false + operation = new LimitOrderCreateOperation(seller, amountToSell, minToReceive, expiration, false); + operation.setFee(new AssetAmount(UnsignedLong.valueOf(2), CORE_ASSET)); + serialized = operation.toBytes(); + Assert.assertArrayEquals("Correct serialization", serialized, Util.hexToBytes("020000000000000000cbe10840787d01000000000001000000000000007984c4bd5800")); + Assert.assertThat("Incorrect serialization", serialized, IsNot.not(IsEqual.equalTo("020000000000000000cbe10840787d01000000000001000000000000007984c4bd5801"))); + } +} \ No newline at end of file From a9a550491b4a0b4b3cd76b6c8a21f1d4fa8e7b13 Mon Sep 17 00:00:00 2001 From: "Nelson R. Perez" Date: Mon, 20 Mar 2017 19:21:04 -0500 Subject: [PATCH 2/7] limit_order_create_operation-bearing tx is successfully being created and accepted by the network --- .../de/bitsharesmunich/graphenej/Main.java | 4 +- .../de/bitsharesmunich/graphenej/Test.java | 6 +- .../api/TransactionBroadcastSequence.java | 2 +- .../operations/LimitOrderCreateOperation.java | 34 +++- .../graphenej/TransactionTest.java | 168 ++++++++++++++++++ .../LimitOrderCreateOperationTest.java | 18 +- 6 files changed, 212 insertions(+), 20 deletions(-) create mode 100644 graphenej/src/test/java/de/bitsharesmunich/graphenej/TransactionTest.java diff --git a/app/src/main/java/de/bitsharesmunich/graphenej/Main.java b/app/src/main/java/de/bitsharesmunich/graphenej/Main.java index 8fcabf3..697ed2d 100644 --- a/app/src/main/java/de/bitsharesmunich/graphenej/Main.java +++ b/app/src/main/java/de/bitsharesmunich/graphenej/Main.java @@ -50,7 +50,7 @@ public class Main { // e.printStackTrace(); // } // test.testCustomSerializer(); - test.testUserAccountSerialization(); +// test.testUserAccountSerialization(); // test.testTransactionSerialization(); // test.testLoginSerialization(); // test.testNetworkBroadcastSerialization(); @@ -89,7 +89,7 @@ public class Main { // test.testGetObjects(); // test.testGetBlockHeader(); // test.testGetLimitOrders(); -// test.testGetTradeHistory(); + test.testGetTradeHistory(); // test.testAssetSerialization(); // test.testGetMarketHistory(); // test.testGetAccountBalances(); diff --git a/app/src/main/java/de/bitsharesmunich/graphenej/Test.java b/app/src/main/java/de/bitsharesmunich/graphenej/Test.java index 42306e2..f0decee 100644 --- a/app/src/main/java/de/bitsharesmunich/graphenej/Test.java +++ b/app/src/main/java/de/bitsharesmunich/graphenej/Test.java @@ -276,12 +276,14 @@ public class Test { @Override public void onSuccess(WitnessResponse response) { System.out.println("onSuccess"); + System.out.println("Callback.Thread. name: "+Thread.currentThread().getName()+", id: "+Thread.currentThread().getId()); } @Override public void onError(BaseResponse.Error error) { System.out.println("onError"); System.out.println(error.data.message); + System.out.println("Callback.Thread. name: "+Thread.currentThread().getName()+", id: "+Thread.currentThread().getId()); } }; @@ -330,7 +332,7 @@ public class Test { mWebSocket.addListener(new TransactionBroadcastSequence(transaction, new Asset("1.3.0"), listener)); mWebSocket.connect(); - + System.out.println("Main.Thread. name: "+Thread.currentThread().getName()+", id: "+Thread.currentThread().getId()); } catch (MalformedOperationException e) { System.out.println("MalformedTransactionException. Msg: " + e.getMessage()); } catch (IOException e) { @@ -1189,7 +1191,7 @@ public class Test { Calendar to = Calendar.getInstance(); to.roll(Calendar.DAY_OF_MONTH, false); - mWebSocket.addListener(new GetTradeHistory("BTS", "EUR", "20161215T0130000", "20161212T233000",100, new WitnessResponseListener() { + mWebSocket.addListener(new GetTradeHistory("BTS", "EUR", "20170309T0130000", "20161212T233000",100, new WitnessResponseListener() { @Override public void onSuccess(WitnessResponse response) { List orders = (List) response.result; 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 748d0b2..bd6bfc3 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/TransactionBroadcastSequence.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/TransactionBroadcastSequence.java @@ -133,7 +133,7 @@ public class TransactionBroadcastSequence extends WebSocketAdapter { websocket.sendText(call.toJsonString()); }else if(baseResponse.id >= BROADCAST_TRANSACTION){ Type WitnessResponseType = new TypeToken>(){}.getType(); - WitnessResponse> witnessResponse = gson.fromJson(response, WitnessResponseType); + WitnessResponse witnessResponse = gson.fromJson(response, WitnessResponseType); mListener.onSuccess(witnessResponse); websocket.disconnect(); } 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 a522542..50a0ad9 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/operations/LimitOrderCreateOperation.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/operations/LimitOrderCreateOperation.java @@ -9,9 +9,19 @@ import de.bitsharesmunich.graphenej.*; import java.nio.ByteBuffer; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.TimeZone; /** * Operation used to denote the creation of a limit order on the blockchain. + * + * The blockchain will atempt to sell amount_to_sell.asset_id for as much min_to_receive.asset_id as possible. + * The fee will be paid by the seller's account. Market fees will apply as specified by the issuer of both the + * selling asset and the receiving asset as a percentage of the amount exchanged. + * + * If either the selling asset or the receiving asset is white list restricted, the order will only be created + * if the seller is on the white list of the restricted asset type. + * + * Market orders are matched in the order they are included in the block chain. */ public class LimitOrderCreateOperation extends BaseOperation { // Number of bytes used for the expiration field. @@ -27,15 +37,22 @@ public class LimitOrderCreateOperation extends BaseOperation { // Inner fields of a limit order private AssetAmount fee; private UserAccount seller; - private AssetAmount toSell; + private AssetAmount amountToSell; private AssetAmount minToReceive; private int expiration; private boolean fillOrKill; + /** + * @param seller: Id of the seller + * @param toSell: Id of the asset to sell + * @param minToReceive: The minimum amount of the asset to receive + * @param expiration: Expiration in seconds + * @param fillOrKill: If this flag is set the entire order must be filled or the operation is rejected. + */ public LimitOrderCreateOperation(UserAccount seller, AssetAmount toSell, AssetAmount minToReceive, int expiration, boolean fillOrKill){ super(OperationType.LIMIT_ORDER_CREATE_OPERATION); this.seller = seller; - this.toSell = toSell; + this.amountToSell = toSell; this.minToReceive = minToReceive; this.expiration = expiration; this.fillOrKill = fillOrKill; @@ -54,11 +71,14 @@ public class LimitOrderCreateOperation extends BaseOperation { if(fee != null) jsonObject.add(KEY_FEE, fee.toJsonObject()); jsonObject.addProperty(KEY_SELLER, seller.toJsonString()); - jsonObject.add(KEY_AMOUNT_TO_SELL, toSell.toJsonObject()); + jsonObject.add(KEY_AMOUNT_TO_SELL, amountToSell.toJsonObject()); jsonObject.add(KEY_MIN_TO_RECEIVE, minToReceive.toJsonObject()); SimpleDateFormat simpleDateFormat = new SimpleDateFormat(Util.TIME_DATE_FORMAT); - jsonObject.addProperty(KEY_EXPIRATION, simpleDateFormat.format(new Date(expiration))); + simpleDateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); + + jsonObject.addProperty(KEY_EXPIRATION, simpleDateFormat.format(new Date(((long) expiration) * 1000))); + jsonObject.addProperty(KEY_FILL_OR_KILL, this.fillOrKill ? "true" : "false"); jsonObject.add(KEY_EXTENSIONS, new JsonArray()); array.add(jsonObject); return array; @@ -73,7 +93,7 @@ public class LimitOrderCreateOperation extends BaseOperation { public byte[] toBytes() { byte[] feeBytes = this.fee.toBytes(); byte[] sellerBytes = this.seller.toBytes(); - byte[] amountBytes = this.toSell.toBytes(); + byte[] amountBytes = this.amountToSell.toBytes(); byte[] minAmountBytes = this.minToReceive.toBytes(); ByteBuffer buffer = ByteBuffer.allocate(EXPIRATION_BYTE_LENGTH); @@ -81,6 +101,8 @@ public class LimitOrderCreateOperation extends BaseOperation { byte[] expirationBytes = Util.revertBytes(buffer.array()); byte[] fillOrKill = this.fillOrKill ? new byte[]{ 0x1 } : new byte[]{ 0x0 }; - return Bytes.concat(feeBytes, sellerBytes, amountBytes, minAmountBytes, expirationBytes, fillOrKill); + byte[] extensions = this.extensions.toBytes(); + + return Bytes.concat(feeBytes, sellerBytes, amountBytes, minAmountBytes, expirationBytes, fillOrKill, extensions); } } diff --git a/graphenej/src/test/java/de/bitsharesmunich/graphenej/TransactionTest.java b/graphenej/src/test/java/de/bitsharesmunich/graphenej/TransactionTest.java new file mode 100644 index 0000000..15aee31 --- /dev/null +++ b/graphenej/src/test/java/de/bitsharesmunich/graphenej/TransactionTest.java @@ -0,0 +1,168 @@ +package de.bitsharesmunich.graphenej; + +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 de.bitsharesmunich.graphenej.api.TransactionBroadcastSequence; +import de.bitsharesmunich.graphenej.interfaces.WitnessResponseListener; +import de.bitsharesmunich.graphenej.models.BaseResponse; +import de.bitsharesmunich.graphenej.models.WitnessResponse; +import de.bitsharesmunich.graphenej.objects.Memo; +import de.bitsharesmunich.graphenej.operations.LimitOrderCreateOperation; +import de.bitsharesmunich.graphenej.operations.LimitOrderCreateOperationTest; +import de.bitsharesmunich.graphenej.operations.TransferOperation; +import de.bitsharesmunich.graphenej.operations.TransferOperationBuilder; +import de.bitsharesmunich.graphenej.test.NaiveSSLContext; +import org.bitcoinj.core.ECKey; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import javax.net.ssl.SSLContext; +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * Created by nelson on 3/6/17. + */ +public class TransactionTest { + private final String BILTHON_15_BRAIN_KEY = System.getenv("BILTHON_15_BRAINKEY"); + private final String BILTHON_5_BRAIN_KEY = System.getenv("BILTHON_5_BRAINKEY"); + private final String BILTHON_16_BRAIN_KEY = System.getenv("BILTHON_16_BRAINKEY"); + + private final String BLOCK_PAY_DE = System.getenv("BLOCKPAY_DE"); + private final String BLOCK_PAY_FR = System.getenv("BLOCKPAY_FR"); + + // Transfer operation transaction + private final Asset CORE_ASSET = new Asset("1.3.0"); + private final UserAccount bilthon_15 = new UserAccount("1.2.143563"); + private final UserAccount bilthon_5 = new UserAccount("1.2.139313"); + private final UserAccount bilthon_16 = new UserAccount("1.2.143569"); + + // Limit order create transaction + private final Asset BIT_USD = new Asset("1.3.121"); + private UserAccount seller = bilthon_15; + private AssetAmount amountToSell = new AssetAmount(UnsignedLong.valueOf(100000), CORE_ASSET); + private AssetAmount minToReceive = new AssetAmount(UnsignedLong.valueOf(520), BIT_USD); + private long expiration; + + WitnessResponseListener listener = new WitnessResponseListener() { + @Override + public void onSuccess(WitnessResponse response) { + System.out.println("onSuccess"); + WitnessResponse witnessResponse = response; + Assert.assertNull(witnessResponse.result); + synchronized (this){ + notifyAll(); + } + } + + @Override + public void onError(BaseResponse.Error error) { + System.out.println("onError"); + System.out.println(error.data.message); + Assert.assertNull(error); + synchronized (this){ + notifyAll(); + } + } + }; + + @Before + public void setup(){ + + } + + /** + * Receives the elements required for building a transaction, puts them together and broadcasts it. + * @param privateKey: The private key used to sign the transaction. + * @param operationList: The list of operations to include + * @param responseListener: The response listener. + */ + private void broadcastTransaction(ECKey privateKey, List operationList, WitnessResponseListener responseListener) { + 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(BLOCK_PAY_DE); + + mWebSocket.addListener(new TransactionBroadcastSequence(transaction, CORE_ASSET, listener)); + mWebSocket.connect(); + synchronized (responseListener){ + responseListener.wait(); + System.out.println("Wait released"); + } + }catch(NoSuchAlgorithmException e){ + System.out.println("NoSuchAlgoritmException. Msg: " + e.getMessage()); + } catch (InterruptedException e) { + System.out.println("InterruptedException. Msg: "+e.getMessage()); + } catch (IOException e) { + System.out.println("IOException. Msg: " + e.getMessage()); + } catch (WebSocketException e) { + System.out.println("WebSocketException. Msg: " + e.getMessage()); + } + } + + @Test + public void testTransferTransaction(){ + ECKey sourcePrivateKey = new BrainKey(BILTHON_15_BRAIN_KEY, 0).getPrivateKey(); + PublicKey to1 = new PublicKey(ECKey.fromPublicOnly(new BrainKey(BILTHON_5_BRAIN_KEY, 0).getPublicKey())); + PublicKey to2 = new PublicKey(ECKey.fromPublicOnly(new BrainKey(BILTHON_16_BRAIN_KEY, 0).getPublicKey())); + + // Creating memo + long nonce = 1; + byte[] encryptedMessage = Memo.encryptMessage(sourcePrivateKey, to1, nonce, "another message"); + Memo memo = new Memo(new Address(ECKey.fromPublicOnly(sourcePrivateKey.getPubKey())), new Address(to1.getKey()), nonce, encryptedMessage); + + // Creating operation 1 + TransferOperation transferOperation1 = new TransferOperationBuilder() + .setTransferAmount(new AssetAmount(UnsignedLong.valueOf(1), CORE_ASSET)) + .setSource(bilthon_15) + .setDestination(bilthon_5) // bilthon-5 + .setFee(new AssetAmount(UnsignedLong.valueOf(264174), CORE_ASSET)) + .build(); + + // Creating operation 2 + TransferOperation transferOperation2 = new TransferOperationBuilder() + .setTransferAmount(new AssetAmount(UnsignedLong.valueOf(1), CORE_ASSET)) + .setSource(bilthon_15) // bilthon-15 + .setDestination(bilthon_16) // bilthon-16 + .setFee(new AssetAmount(UnsignedLong.valueOf(264174), CORE_ASSET)) + .build(); + + + // Adding operations to the operation list + ArrayList operationList = new ArrayList<>(); + operationList.add(transferOperation1); + operationList.add(transferOperation2); + + // Broadcasting transaction + broadcastTransaction(sourcePrivateKey, operationList, listener); + } + + @Test + public void testLimitOrderCreateTransaction(){ + ECKey privateKey = new BrainKey(BILTHON_15_BRAIN_KEY, 0).getPrivateKey(); + expiration = (System.currentTimeMillis() / 1000) + 60 * 60; + + // Creating limit order creation operation + LimitOrderCreateOperation operation = new LimitOrderCreateOperation(seller, amountToSell, minToReceive, (int) expiration, false); + operation.setFee(new AssetAmount(UnsignedLong.valueOf(2), CORE_ASSET)); + + ArrayList operationList = new ArrayList<>(); + operationList.add(operation); + + // Broadcasting transaction + broadcastTransaction(privateKey, operationList, listener); + } +} \ No newline at end of file diff --git a/graphenej/src/test/java/de/bitsharesmunich/graphenej/operations/LimitOrderCreateOperationTest.java b/graphenej/src/test/java/de/bitsharesmunich/graphenej/operations/LimitOrderCreateOperationTest.java index 52fc9cb..0a626db 100644 --- a/graphenej/src/test/java/de/bitsharesmunich/graphenej/operations/LimitOrderCreateOperationTest.java +++ b/graphenej/src/test/java/de/bitsharesmunich/graphenej/operations/LimitOrderCreateOperationTest.java @@ -17,11 +17,11 @@ import static org.junit.Assert.*; * Created by nelson on 3/6/17. */ public class LimitOrderCreateOperationTest { - private final int AMOUNT_TO_SELL = 25000000; - private final int MIN_TO_RECEIVE = 1; - private final Asset CORE_ASSET = new Asset("1.3.0"); - private final Asset BIT_USD = new Asset("1.3.121"); - private final int DEFAULT_EXPIRATION = 1488831620; // 2017-03-06T20:20:20 + private static final int AMOUNT_TO_SELL = 25000000; + private static final int MIN_TO_RECEIVE = 1; + private static final Asset CORE_ASSET = new Asset("1.3.0"); + private static final Asset BIT_USD = new Asset("1.3.121"); + private static final int DEFAULT_EXPIRATION = 1488831620; // 2017-03-06T20:20:20 private UserAccount seller; private AssetAmount amountToSell; @@ -43,14 +43,14 @@ public class LimitOrderCreateOperationTest { LimitOrderCreateOperation operation = new LimitOrderCreateOperation(seller, amountToSell, minToReceive, expiration, true); operation.setFee(new AssetAmount(UnsignedLong.valueOf(2), CORE_ASSET)); byte[] serialized = operation.toBytes(); - Assert.assertArrayEquals("Correct serialization", serialized, Util.hexToBytes("020000000000000000cbe10840787d01000000000001000000000000007984c4bd5801")); - Assert.assertThat("Incorrect serialization", serialized, IsNot.not(IsEqual.equalTo("020000000000000000cbe10840787d01000000000001000000000000007984c4bd5800"))); + Assert.assertArrayEquals("Correct serialization", serialized, Util.hexToBytes("020000000000000000cbe10840787d01000000000001000000000000007984c4bd580100")); + Assert.assertThat("Incorrect serialization", serialized, IsNot.not(IsEqual.equalTo("020000000000000000cbe10840787d01000000000001000000000000007984c4bd580000"))); // Testing serialization of operation with fillOrKill parameter == false operation = new LimitOrderCreateOperation(seller, amountToSell, minToReceive, expiration, false); operation.setFee(new AssetAmount(UnsignedLong.valueOf(2), CORE_ASSET)); serialized = operation.toBytes(); - Assert.assertArrayEquals("Correct serialization", serialized, Util.hexToBytes("020000000000000000cbe10840787d01000000000001000000000000007984c4bd5800")); - Assert.assertThat("Incorrect serialization", serialized, IsNot.not(IsEqual.equalTo("020000000000000000cbe10840787d01000000000001000000000000007984c4bd5801"))); + Assert.assertArrayEquals("Correct serialization", serialized, Util.hexToBytes("020000000000000000cbe10840787d01000000000001000000000000007984c4bd580000")); + Assert.assertThat("Incorrect serialization", serialized, IsNot.not(IsEqual.equalTo("020000000000000000cbe10840787d01000000000001000000000000007984c4bd580100"))); } } \ No newline at end of file From aa642edce9188185a68695e196244291beb88dc5 Mon Sep 17 00:00:00 2001 From: "Nelson R. Perez" Date: Wed, 22 Mar 2017 16:25:55 -0500 Subject: [PATCH 3/7] Added support for the limit_order_cancel_operation --- .../de/bitsharesmunich/graphenej/Main.java | 4 +- .../de/bitsharesmunich/graphenej/Test.java | 10 +- .../graphenej/AssetAmount.java | 3 + .../graphenej/BaseOperation.java | 8 +- .../bitsharesmunich/graphenej/LimitOrder.java | 131 ++++++++++++- .../graphenej/api/GetLimitOrders.java | 1 + .../operations/LimitOrderCancelOperation.java | 61 +++++++ .../operations/LimitOrderCreateOperation.java | 3 +- .../graphenej/TransactionTest.java | 172 ++++++++++++++++-- .../LimitOrderCancelOperationTest.java | 32 ++++ 10 files changed, 395 insertions(+), 30 deletions(-) create mode 100644 graphenej/src/main/java/de/bitsharesmunich/graphenej/operations/LimitOrderCancelOperation.java create mode 100644 graphenej/src/test/java/de/bitsharesmunich/graphenej/operations/LimitOrderCancelOperationTest.java diff --git a/app/src/main/java/de/bitsharesmunich/graphenej/Main.java b/app/src/main/java/de/bitsharesmunich/graphenej/Main.java index 697ed2d..19bd618 100644 --- a/app/src/main/java/de/bitsharesmunich/graphenej/Main.java +++ b/app/src/main/java/de/bitsharesmunich/graphenej/Main.java @@ -88,8 +88,8 @@ public class Main { // test.testListAssets(); // test.testGetObjects(); // test.testGetBlockHeader(); -// test.testGetLimitOrders(); - test.testGetTradeHistory(); + test.testGetLimitOrders(); +// test.testGetTradeHistory(); // test.testAssetSerialization(); // test.testGetMarketHistory(); // test.testGetAccountBalances(); diff --git a/app/src/main/java/de/bitsharesmunich/graphenej/Test.java b/app/src/main/java/de/bitsharesmunich/graphenej/Test.java index f0decee..962d841 100644 --- a/app/src/main/java/de/bitsharesmunich/graphenej/Test.java +++ b/app/src/main/java/de/bitsharesmunich/graphenej/Test.java @@ -1137,7 +1137,7 @@ public class Test { WebSocket mWebSocket = factory.createSocket(BLOCK_PAY_DE); - Asset base = new Asset("1.3.120", "EUR", 4); + Asset base = new Asset("1.3.0", "BTS", 4); Asset quote = new Asset("1.3.121", "USD", 4); mWebSocket.addListener(new GetLimitOrders(base.getObjectId(), quote.getObjectId(), 100, new WitnessResponseListener() { @@ -1150,10 +1150,10 @@ public class Test { // System.out.println(String.format("base: %d, quote: %d", // order.sell_price.base.getAmount().longValue(), // order.sell_price.quote.getAmount().longValue())); - order.sell_price.base.getAsset().setPrecision(base.getPrecision()); - order.sell_price.quote.getAsset().setPrecision(quote.getPrecision()); - double baseToQuoteExchange = converter.getConversionRate(order.sell_price, Converter.BASE_TO_QUOTE); - double quoteToBaseExchange = converter.getConversionRate(order.sell_price, Converter.QUOTE_TO_BASE); + order.getSellPrice().base.getAsset().setPrecision(base.getPrecision()); + order.getSellPrice().quote.getAsset().setPrecision(quote.getPrecision()); + double baseToQuoteExchange = converter.getConversionRate(order.getSellPrice(), Converter.BASE_TO_QUOTE); + double quoteToBaseExchange = converter.getConversionRate(order.getSellPrice(), Converter.QUOTE_TO_BASE); System.out.println(String.format("base to quote: %.5f, quote to base: %.5f", baseToQuoteExchange, quoteToBaseExchange)); } } diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/AssetAmount.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/AssetAmount.java index 135d7d8..76abc69 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/AssetAmount.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/AssetAmount.java @@ -129,6 +129,9 @@ public class AssetAmount implements ByteSerializable, JsonSerializable { } } + /** + * Custom deserializer used for this class + */ public static class AssetAmountDeserializer implements JsonDeserializer { @Override diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/BaseOperation.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/BaseOperation.java index 36bdf10..9a727cd 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/BaseOperation.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/BaseOperation.java @@ -1,5 +1,7 @@ package de.bitsharesmunich.graphenej; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; import de.bitsharesmunich.graphenej.interfaces.ByteSerializable; import de.bitsharesmunich.graphenej.interfaces.JsonSerializable; @@ -25,5 +27,9 @@ public abstract class BaseOperation implements ByteSerializable, JsonSerializabl public abstract void setFee(AssetAmount assetAmount); - public abstract byte[] toBytes(); + public JsonElement toJsonObject(){ + JsonArray array = new JsonArray(); + array.add(this.getId()); + return array; + } } diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/LimitOrder.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/LimitOrder.java index 010a5e3..af950c8 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/LimitOrder.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/LimitOrder.java @@ -1,14 +1,131 @@ package de.bitsharesmunich.graphenej; +import com.google.gson.*; +import de.bitsharesmunich.graphenej.interfaces.ByteSerializable; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.lang.reflect.Type; + /** * * @author henry */ -public class LimitOrder { - public String id; - public String expiration; - public UserAccount seller; - public long for_sale; - public long deferred_fee; - public Price sell_price; +public class LimitOrder extends GrapheneObject implements ByteSerializable { + + public static final String KEY_EXPIRATION = "expiration"; + public static final String KEY_SELLER = "seller"; + public static final String KEY_FOR_SALE = "for_sale"; + public static final String KEY_DEFERRED_FEE = "deferred_fee"; + public static final String KEY_PRICE = "key_price"; + + private String expiration; + private UserAccount seller; + private long forSale; + private long deferredFee; + private Price sellPrice; + + public LimitOrder(String id) { + super(id); + } + + public String getExpiration() { + return expiration; + } + + public void setExpiration(String expiration) { + this.expiration = expiration; + } + + public UserAccount getSeller() { + return seller; + } + + public void setSeller(UserAccount seller) { + this.seller = seller; + } + + public long getForSale() { + return forSale; + } + + public void setForSale(long forSale) { + this.forSale = forSale; + } + + public long getDeferredFee() { + return deferredFee; + } + + public void setDeferredFee(long deferredFee) { + this.deferredFee = deferredFee; + } + + public Price getSellPrice() { + return sellPrice; + } + + public void setSellPrice(Price sellPrice) { + this.sellPrice = sellPrice; + } + + @Override + public byte[] toBytes() { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + DataOutput out = new DataOutputStream(byteArrayOutputStream); + byte[] serialized = null; + try { + Varint.writeUnsignedVarLong(this.instance, out); + serialized = byteArrayOutputStream.toByteArray(); + } catch (IOException e) { + e.printStackTrace(); + } + return serialized; + } + + /** + * Custom deserializer for the LimitOrder class, used to deserialize a json-formatted string in + * the following format: + * + * { + * "id": "1.7.2389233", + * "expiration": "2017-04-21T15:40:04", + * "seller": "1.2.114363", + * "forSale": "10564959415", + * "sell_price": { + * "base": { + * "amount": "10565237932", + * "asset_id": "1.3.0" + * }, + * "quote": { + * "amount": 5803878, + * "asset_id": "1.3.121" + * } + * }, + * "deferredFee": 0 + * } + */ + public static class LimitOrderDeserializer implements JsonDeserializer { + + @Override + public LimitOrder deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + JsonObject object = json.getAsJsonObject(); + String id = object.get(KEY_ID).getAsString(); + String expiration = object.get(KEY_EXPIRATION).getAsString(); + UserAccount seller = context.deserialize(object.get(KEY_SELLER), UserAccount.class); + String forSale = object.get(KEY_FOR_SALE).getAsString(); + Price price = context.deserialize(object.get(KEY_PRICE), Price.class); + long deferredFee = object.get(KEY_DEFERRED_FEE).getAsLong(); + + LimitOrder limitOrder = new LimitOrder(id); + limitOrder.setExpiration(expiration); + limitOrder.setSeller(seller); + limitOrder.setForSale(Long.parseLong(forSale)); + limitOrder.setSellPrice(price); + limitOrder.setDeferredFee(deferredFee); + return limitOrder; + } + } } 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 d018661..60c7f4d 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetLimitOrders.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/GetLimitOrders.java @@ -59,6 +59,7 @@ public class GetLimitOrders extends WebSocketAdapter { Type GetLimitOrdersResponse = new TypeToken>>() {}.getType(); builder.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer()); builder.registerTypeAdapter(UserAccount.class, new UserAccount.UserAccountSimpleDeserializer()); + builder.registerTypeAdapter(LimitOrder.class, new LimitOrder.LimitOrderDeserializer()); WitnessResponse> witnessResponse = builder.create().fromJson(response, GetLimitOrdersResponse); if (witnessResponse.error != null) { this.mListener.onError(witnessResponse.error); diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/operations/LimitOrderCancelOperation.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/operations/LimitOrderCancelOperation.java new file mode 100644 index 0000000..0334255 --- /dev/null +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/operations/LimitOrderCancelOperation.java @@ -0,0 +1,61 @@ +package de.bitsharesmunich.graphenej.operations; + +import com.google.common.primitives.Bytes; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import de.bitsharesmunich.graphenej.*; + +/** + * Created by nelson on 3/21/17. + */ +public class LimitOrderCancelOperation extends BaseOperation { + + // Constants used in the JSON representation + public static final String KEY_FEE_PAYING_ACCOUNT = "fee_paying_account"; + public static final String KEY_ORDER_ID = "order"; + + + public LimitOrderCancelOperation(LimitOrder order, UserAccount feePayingAccount) { + super(OperationType.LIMIT_ORDER_CANCEL_OPERATION); + this.order = order; + this.feePayingAccount = feePayingAccount; + } + + // Inner fields of a limit order cancel operation + private AssetAmount fee; + private UserAccount feePayingAccount; + private LimitOrder order; + + @Override + public String toJsonString() { + return null; + } + + @Override + public JsonElement toJsonObject() { + JsonArray array = (JsonArray) super.toJsonObject(); + JsonObject jsonObject = new JsonObject(); + if(fee != null) + jsonObject.add(KEY_FEE, fee.toJsonObject()); + jsonObject.addProperty(KEY_FEE_PAYING_ACCOUNT, feePayingAccount.getObjectId()); + jsonObject.addProperty(KEY_ORDER_ID, order.getObjectId()); + jsonObject.add(KEY_EXTENSIONS, new JsonArray()); + array.add(jsonObject); + return array; + } + + @Override + public void setFee(AssetAmount assetAmount) { + this.fee = assetAmount; + } + + @Override + public byte[] toBytes() { + byte[] feeBytes = this.fee.toBytes(); + byte[] feePayingAccountBytes = this.feePayingAccount.toBytes(); + byte[] orderIdBytes = this.order.toBytes(); + byte[] extensions = this.extensions.toBytes(); + return Bytes.concat(feeBytes, feePayingAccountBytes, orderIdBytes, extensions); + } +} 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 50a0ad9..7b60c70 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/operations/LimitOrderCreateOperation.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/operations/LimitOrderCreateOperation.java @@ -65,8 +65,7 @@ public class LimitOrderCreateOperation extends BaseOperation { @Override public JsonElement toJsonObject() { - JsonArray array = new JsonArray(); - array.add(this.getId()); + JsonArray array = (JsonArray) super.toJsonObject(); JsonObject jsonObject = new JsonObject(); if(fee != null) jsonObject.add(KEY_FEE, fee.toJsonObject()); diff --git a/graphenej/src/test/java/de/bitsharesmunich/graphenej/TransactionTest.java b/graphenej/src/test/java/de/bitsharesmunich/graphenej/TransactionTest.java index 15aee31..b0c00af 100644 --- a/graphenej/src/test/java/de/bitsharesmunich/graphenej/TransactionTest.java +++ b/graphenej/src/test/java/de/bitsharesmunich/graphenej/TransactionTest.java @@ -4,15 +4,13 @@ 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 de.bitsharesmunich.graphenej.api.GetLimitOrders; import de.bitsharesmunich.graphenej.api.TransactionBroadcastSequence; import de.bitsharesmunich.graphenej.interfaces.WitnessResponseListener; import de.bitsharesmunich.graphenej.models.BaseResponse; import de.bitsharesmunich.graphenej.models.WitnessResponse; import de.bitsharesmunich.graphenej.objects.Memo; -import de.bitsharesmunich.graphenej.operations.LimitOrderCreateOperation; -import de.bitsharesmunich.graphenej.operations.LimitOrderCreateOperationTest; -import de.bitsharesmunich.graphenej.operations.TransferOperation; -import de.bitsharesmunich.graphenej.operations.TransferOperationBuilder; +import de.bitsharesmunich.graphenej.operations.*; import de.bitsharesmunich.graphenej.test.NaiveSSLContext; import org.bitcoinj.core.ECKey; import org.junit.Assert; @@ -50,14 +48,22 @@ public class TransactionTest { private AssetAmount minToReceive = new AssetAmount(UnsignedLong.valueOf(520), BIT_USD); private long expiration; + private static final class Lock { } + private final Object lockObject = new Lock(); + + /** + * Generic witness response listener that will just release the lock created in + * main thread. + */ WitnessResponseListener listener = new WitnessResponseListener() { + @Override public void onSuccess(WitnessResponse response) { System.out.println("onSuccess"); WitnessResponse witnessResponse = response; Assert.assertNull(witnessResponse.result); synchronized (this){ - notifyAll(); + this.notifyAll(); } } @@ -74,7 +80,6 @@ public class TransactionTest { @Before public void setup(){ - } /** @@ -82,8 +87,9 @@ public class TransactionTest { * @param privateKey: The private key used to sign the transaction. * @param operationList: The list of operations to include * @param responseListener: The response listener. + * @param lockObject: Optional object to use as a lock */ - private void broadcastTransaction(ECKey privateKey, List operationList, WitnessResponseListener responseListener) { + private void broadcastTransaction(ECKey privateKey, List operationList, WitnessResponseListener responseListener, Object lockObject) { try{ Transaction transaction = new Transaction(privateKey, null, operationList); @@ -96,11 +102,19 @@ public class TransactionTest { WebSocket mWebSocket = factory.createSocket(BLOCK_PAY_DE); - mWebSocket.addListener(new TransactionBroadcastSequence(transaction, CORE_ASSET, listener)); + mWebSocket.addListener(new TransactionBroadcastSequence(transaction, CORE_ASSET, responseListener)); mWebSocket.connect(); - synchronized (responseListener){ - responseListener.wait(); - System.out.println("Wait released"); + + // If a lock object is specified, we use it + if(lockObject != null){ + synchronized (lockObject){ + lockObject.wait(); + } + }else{ + // Otherwise we just use this listener as the lock + synchronized (this){ + this.wait(); + } } }catch(NoSuchAlgorithmException e){ System.out.println("NoSuchAlgoritmException. Msg: " + e.getMessage()); @@ -147,7 +161,7 @@ public class TransactionTest { operationList.add(transferOperation2); // Broadcasting transaction - broadcastTransaction(sourcePrivateKey, operationList, listener); + broadcastTransaction(sourcePrivateKey, operationList, listener, null); } @Test @@ -163,6 +177,138 @@ public class TransactionTest { operationList.add(operation); // Broadcasting transaction - broadcastTransaction(privateKey, operationList, listener); + broadcastTransaction(privateKey, operationList, listener, null); + } + + /** + * Since tests should be independent of each other, in order to be able to test the cancellation of an + * existing order we must first proceed to create one. And after creating one, we must also retrieve + * its id in a separate call. + * + * All of this just makes this test a bit more complex, since we have 3 clearly defined tasks that require + * network communication + * + * 1- Create order + * 2- Retrieve order id + * 3- Send order cancellation tx + * + * Only the last one is what we actually want to test + * + * @throws NoSuchAlgorithmException + * @throws IOException + * @throws WebSocketException + */ + @Test + public void testLimitOrderCancelTransaction() throws NoSuchAlgorithmException, IOException, WebSocketException { + + // We first must create a limit order for this test + ECKey privateKey = new BrainKey(BILTHON_15_BRAIN_KEY, 0).getPrivateKey(); + expiration = (System.currentTimeMillis() / 1000) + 60 * 5; + + // Creating limit order creation operation + LimitOrderCreateOperation operation = new LimitOrderCreateOperation(seller, amountToSell, minToReceive, (int) expiration, false); + operation.setFee(new AssetAmount(UnsignedLong.valueOf(2), CORE_ASSET)); + + ArrayList operationList = new ArrayList<>(); + operationList.add(operation); + + // Broadcasting transaction (Task 1) + broadcastTransaction(privateKey, operationList, new WitnessResponseListener() { + + @Override + public void onSuccess(WitnessResponse response) { + + System.out.println("onSuccess.0"); + try{ + // Setting up the assets + Asset base = amountToSell.getAsset(); + Asset quote = minToReceive.getAsset(); + + SSLContext context = NaiveSSLContext.getInstance("TLS"); + WebSocketFactory factory = new WebSocketFactory(); + + // Set the custom SSL context. + factory.setSSLContext(context); + WebSocket mWebSocket = factory.createSocket(BLOCK_PAY_DE); + + // Requesting limit order to cancel (Task 2) + mWebSocket.addListener(new GetLimitOrders(base.getObjectId(), quote.getObjectId(), 100, new WitnessResponseListener() { + + @Override + public void onSuccess(WitnessResponse response) { + System.out.println("onSuccess.1"); + List orders = (List) response.result; + for(LimitOrder order : orders){ + if(order.getSeller().getObjectId().equals(bilthon_15.getObjectId())){ + + // Instantiating a private key for bilthon-15 + ECKey privateKey = new BrainKey(BILTHON_15_BRAIN_KEY, 0).getPrivateKey(); + + // Creating limit order cancellation operation + LimitOrderCancelOperation operation = new LimitOrderCancelOperation(order, bilthon_15); + ArrayList operationList = new ArrayList<>(); + operationList.add(operation); + + // Broadcasting order cancellation tx (Task 3) + broadcastTransaction(privateKey, operationList, new WitnessResponseListener() { + + @Override + public void onSuccess(WitnessResponse response) { + System.out.println("onSuccess.2"); + Assert.assertNull(response.result); + synchronized (this){ + notifyAll(); + } + synchronized (lockObject){ + lockObject.notifyAll(); + } + } + + @Override + public void onError(BaseResponse.Error error) { + System.out.println("onError.2"); + Assert.assertNull(error); + synchronized (this){ + notifyAll(); + } + synchronized (lockObject){ + lockObject.notifyAll(); + } + } + }, null); + } + } + } + + @Override + public void onError(BaseResponse.Error error) { + System.out.println("onError.1"); + System.out.println(error.data.message); + Assert.assertNull(error); + synchronized (lockObject){ + lockObject.notifyAll(); + } + } + })); + + mWebSocket.connect(); + + }catch(NoSuchAlgorithmException e){ + System.out.println("NoSuchAlgorithmException. Msg: "+e.getMessage()); + } catch (WebSocketException e) { + System.out.println("WebSocketException. Msg: "+e.getMessage()); + } catch (IOException e) { + System.out.println("IOException. Msg: "+e.getMessage()); + } + } + + @Override + public void onError(BaseResponse.Error error) { + System.out.println("OnError. Msg: "+error.message); + synchronized (this){ + notifyAll(); + } + } + }, lockObject); } } \ No newline at end of file diff --git a/graphenej/src/test/java/de/bitsharesmunich/graphenej/operations/LimitOrderCancelOperationTest.java b/graphenej/src/test/java/de/bitsharesmunich/graphenej/operations/LimitOrderCancelOperationTest.java new file mode 100644 index 0000000..a065d45 --- /dev/null +++ b/graphenej/src/test/java/de/bitsharesmunich/graphenej/operations/LimitOrderCancelOperationTest.java @@ -0,0 +1,32 @@ +package de.bitsharesmunich.graphenej.operations; + +import com.google.common.primitives.UnsignedLong; +import de.bitsharesmunich.graphenej.*; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertArrayEquals; + +/** + * Created by nelson on 3/21/17. + */ +public class LimitOrderCancelOperationTest { + private static final Asset CORE_ASSET = new Asset("1.3.0"); + private UserAccount feePayingAccount; + private LimitOrder limitOrder; + + @Before + public void setup(){ + feePayingAccount = new UserAccount("1.2.143563"); + limitOrder = new LimitOrder("1.7.2360289"); + } + + @Test + public void toBytes() throws Exception { + LimitOrderCancelOperation operation = new LimitOrderCancelOperation(limitOrder, feePayingAccount); + operation.setFee(new AssetAmount(UnsignedLong.valueOf(2), CORE_ASSET)); + byte[] serialized = operation.toBytes(); + assertArrayEquals("Correct serialization", Util.hexToBytes("020000000000000000cbe108e187900100"), serialized); + } +} \ No newline at end of file From d7019a3689f6db477519f083e4bdcb8b60b572ee Mon Sep 17 00:00:00 2001 From: "Nelson R. Perez" Date: Wed, 22 Mar 2017 16:59:11 -0500 Subject: [PATCH 4/7] Adjusting tests --- .../graphenej/TransactionTest.java | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/graphenej/src/test/java/de/bitsharesmunich/graphenej/TransactionTest.java b/graphenej/src/test/java/de/bitsharesmunich/graphenej/TransactionTest.java index b0c00af..f82cd2b 100644 --- a/graphenej/src/test/java/de/bitsharesmunich/graphenej/TransactionTest.java +++ b/graphenej/src/test/java/de/bitsharesmunich/graphenej/TransactionTest.java @@ -48,9 +48,13 @@ public class TransactionTest { private AssetAmount minToReceive = new AssetAmount(UnsignedLong.valueOf(520), BIT_USD); private long expiration; + // Lock object private static final class Lock { } private final Object lockObject = new Lock(); + // Response + private BaseResponse baseResponse; + /** * Generic witness response listener that will just release the lock created in * main thread. @@ -60,8 +64,7 @@ public class TransactionTest { @Override public void onSuccess(WitnessResponse response) { System.out.println("onSuccess"); - WitnessResponse witnessResponse = response; - Assert.assertNull(witnessResponse.result); + baseResponse = response; synchronized (this){ this.notifyAll(); } @@ -69,9 +72,7 @@ public class TransactionTest { @Override public void onError(BaseResponse.Error error) { - System.out.println("onError"); - System.out.println(error.data.message); - Assert.assertNull(error); + System.out.println("onError. Msg: "+error.data.message); synchronized (this){ notifyAll(); } @@ -112,10 +113,12 @@ public class TransactionTest { } }else{ // Otherwise we just use this listener as the lock - synchronized (this){ - this.wait(); + synchronized (responseListener){ + responseListener.wait(); } } + Assert.assertNotNull(baseResponse); + Assert.assertNull(baseResponse.error); }catch(NoSuchAlgorithmException e){ System.out.println("NoSuchAlgoritmException. Msg: " + e.getMessage()); } catch (InterruptedException e) { @@ -255,7 +258,7 @@ public class TransactionTest { @Override public void onSuccess(WitnessResponse response) { System.out.println("onSuccess.2"); - Assert.assertNull(response.result); + baseResponse = response; synchronized (this){ notifyAll(); } @@ -267,7 +270,6 @@ public class TransactionTest { @Override public void onError(BaseResponse.Error error) { System.out.println("onError.2"); - Assert.assertNull(error); synchronized (this){ notifyAll(); } From c6e4b9ea5b070c8629563e528b4189a376f31ea2 Mon Sep 17 00:00:00 2001 From: "Nelson R. Perez" Date: Wed, 22 Mar 2017 22:54:16 -0500 Subject: [PATCH 5/7] Small changes introduced in order to publish the library to the Maven Central Repository --- .gitignore | 9 +++ gradle.properties | 33 ++++++++++ graphenej/build.gradle | 13 +--- graphenej/gradle.properties | 3 + graphenej/maven-push.gradle | 112 ++++++++++++++++++++++++++++++++++ graphenej/old.settings.gradle | 3 - 6 files changed, 159 insertions(+), 14 deletions(-) create mode 100644 gradle.properties create mode 100644 graphenej/gradle.properties create mode 100644 graphenej/maven-push.gradle delete mode 100644 graphenej/old.settings.gradle diff --git a/.gitignore b/.gitignore index 9c34a1a..a8556fc 100644 --- a/.gitignore +++ b/.gitignore @@ -83,3 +83,12 @@ src/main/java/com/luminiasoft/bitshares/mycelium/* # Ignore bin backups *.bin + +# [Maven] +# ------- +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..685409a --- /dev/null +++ b/gradle.properties @@ -0,0 +1,33 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# Default value: -Xmx10248m -XX:MaxPermSize=256m +# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true + +VERSION_NAME=0.4.0 +VERSION_CODE=1 +GROUP=com.github.kenCode-de + +POM_DESCRIPTION=A Java library for mobile app Developers; Graphene/Bitshares blockchain. +POM_URL=https://github.com/kenCode-de/graphenej +POM_SCM_URL=https://github.com/kenCode-de/graphenej +POM_SCM_CONNECTION=scm:git@github.com:kenCode-de/graphenej.git +POM_SCM_DEV_CONNECTION=scm:git@github.com:kenCode-de/graphenej.git +POM_LICENCE_NAME=MIT License +POM_LICENCE_URL=https://github.com/kenCode-de/graphenej/blob/master/LICENSE +POM_LICENCE_DIST=repo +POM_DEVELOPER_ID=bilthon +POM_DEVELOPER_NAME=GitHub FullName \ No newline at end of file diff --git a/graphenej/build.gradle b/graphenej/build.gradle index 8f156ee..d22e299 100644 --- a/graphenej/build.gradle +++ b/graphenej/build.gradle @@ -1,17 +1,8 @@ group 'de.bitsharesmunich' version '0.1-SNAPSHOT' -//apply plugin: 'java' -// -//model { -// components { -// main(JvmLibrarySpec) -// } -//} -// -//repositories { -// mavenCentral() -//} +apply plugin: 'com.android.library' +apply from: 'maven-push.gradle' dependencies { testCompile group: 'junit', name: 'junit', version: '4.12' diff --git a/graphenej/gradle.properties b/graphenej/gradle.properties new file mode 100644 index 0000000..acb7b2c --- /dev/null +++ b/graphenej/gradle.properties @@ -0,0 +1,3 @@ +POM_NAME=Graphenej +POM_ARTIFACT_ID=graphenej +POM_PACKAGING=aar \ No newline at end of file diff --git a/graphenej/maven-push.gradle b/graphenej/maven-push.gradle new file mode 100644 index 0000000..2f96fe1 --- /dev/null +++ b/graphenej/maven-push.gradle @@ -0,0 +1,112 @@ +/* + * Copyright 2013 Chris Banes + * + * 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. + */ + +apply plugin: 'maven' +apply plugin: 'signing' + +def isReleaseBuild() { + return VERSION_NAME.contains("SNAPSHOT") == false +} + +def getReleaseRepositoryUrl() { + return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL + : "https://oss.sonatype.org/service/local/staging/deploy/maven2/" +} + +def getSnapshotRepositoryUrl() { + return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL + : "https://oss.sonatype.org/content/repositories/snapshots/" +} + +def getRepositoryUsername() { + return hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : "" +} + +def getRepositoryPassword() { + return hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : "" +} + +afterEvaluate { project -> + uploadArchives { + repositories { + mavenDeployer { + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + + pom.groupId = GROUP + pom.artifactId = POM_ARTIFACT_ID + pom.version = VERSION_NAME + + repository(url: getReleaseRepositoryUrl()) { + authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) + } + snapshotRepository(url: getSnapshotRepositoryUrl()) { + authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) + } + + pom.project { + name POM_NAME + packaging POM_PACKAGING + description POM_DESCRIPTION + url POM_URL + + scm { + url POM_SCM_URL + connection POM_SCM_CONNECTION + developerConnection POM_SCM_DEV_CONNECTION + } + + licenses { + license { + name POM_LICENCE_NAME + url POM_LICENCE_URL + distribution POM_LICENCE_DIST + } + } + + developers { + developer { + id POM_DEVELOPER_ID + name POM_DEVELOPER_NAME + } + } + } + } + } + } + + signing { + required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") } + sign configurations.archives + } + + //task androidJavadocs(type: Javadoc) { + //source = android.sourceSets.main.allJava + //} + + //task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { + //classifier = 'javadoc' + //from androidJavadocs.destinationDir + //} + + task androidSourcesJar(type: Jar) { + classifier = 'sources' + from android.sourceSets.main.java.sourceFiles + } + + artifacts { + archives androidSourcesJar + } +} \ No newline at end of file diff --git a/graphenej/old.settings.gradle b/graphenej/old.settings.gradle deleted file mode 100644 index b36009e..0000000 --- a/graphenej/old.settings.gradle +++ /dev/null @@ -1,3 +0,0 @@ -rootProject.name = 'graphenej' -include 'application' - From 425cb663b1dad8a5c59ad28cf521955a36f1ddda Mon Sep 17 00:00:00 2001 From: "Nelson R. Perez" Date: Thu, 23 Mar 2017 16:34:11 -0500 Subject: [PATCH 6/7] Introducing some updates required in order to make this library public via maven central --- .gitignore | 15 +- app/app.iml | 9 - app/build.gradle | 3 - app/build/libs/app.jar | Bin 44194 -> 0 bytes app/build/tmp/jar/MANIFEST.MF | 2 - .../de/bitsharesmunich/graphenej/Main.java | 99 -- .../de/bitsharesmunich/graphenej/Test.java | 1423 ----------------- build.gradle | 9 +- gradle.properties | 2 +- graphenej/build.gradle | 24 +- graphenej/src/main/AndroidManifest.xml | 9 + .../AccountUpdateTransactionBuilder.java | 67 - settings.gradle | 2 +- 13 files changed, 55 insertions(+), 1609 deletions(-) delete mode 100644 app/app.iml delete mode 100644 app/build.gradle delete mode 100644 app/build/libs/app.jar delete mode 100644 app/build/tmp/jar/MANIFEST.MF delete mode 100644 app/src/main/java/de/bitsharesmunich/graphenej/Main.java delete mode 100644 app/src/main/java/de/bitsharesmunich/graphenej/Test.java create mode 100644 graphenej/src/main/AndroidManifest.xml delete mode 100644 graphenej/src/main/java/de/bitsharesmunich/graphenej/AccountUpdateTransactionBuilder.java diff --git a/.gitignore b/.gitignore index a8556fc..49270eb 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,9 @@ # Gradle # ------ .gradle +gradle +gradlew +gradlew.bat /build /buildSrc/build /subprojects/*/build @@ -79,8 +82,6 @@ atlassian-ide-plugin.xml # ---- /*.log -src/main/java/com/luminiasoft/bitshares/mycelium/* - # Ignore bin backups *.bin @@ -92,3 +93,13 @@ pom.xml.releaseBackup pom.xml.versionsBackup pom.xml.next release.properties + +# Package Files # +*.jar +*.war +*.ear + +# Build dir +graphenej/build + +local.properties diff --git a/app/app.iml b/app/app.iml deleted file mode 100644 index 8021953..0000000 --- a/app/app.iml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle deleted file mode 100644 index 080b5c4..0000000 --- a/app/build.gradle +++ /dev/null @@ -1,3 +0,0 @@ -dependencies { - compile project(':graphenej') -} diff --git a/app/build/libs/app.jar b/app/build/libs/app.jar deleted file mode 100644 index 1310b06f38f19fdf2fa4583a9e183941c6094894..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44194 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 W$goQ|92_O=fdjktzApaj+y4MWshp4i diff --git a/app/build/tmp/jar/MANIFEST.MF b/app/build/tmp/jar/MANIFEST.MF deleted file mode 100644 index 59499bc..0000000 --- a/app/build/tmp/jar/MANIFEST.MF +++ /dev/null @@ -1,2 +0,0 @@ -Manifest-Version: 1.0 - diff --git a/app/src/main/java/de/bitsharesmunich/graphenej/Main.java b/app/src/main/java/de/bitsharesmunich/graphenej/Main.java deleted file mode 100644 index 19bd618..0000000 --- a/app/src/main/java/de/bitsharesmunich/graphenej/Main.java +++ /dev/null @@ -1,99 +0,0 @@ -package de.bitsharesmunich.graphenej; - -public class Main { - - // Brain key from Nelson's app referencing the bilthon-83 account - public static final String BILTHON_83_BRAIN_KEY = System.getenv("BILTHON_83_BRAIN_KEY"); - - public static final String BILTHON_83_ORIGINAL_BRAIN_KEY = System.getenv("BILTHON_83_ORIGINAL_BRAIN_KEY"); - - public static final String BILTHON_1_BRAIN_KEY = System.getenv("BILTHON_1_BRAIN_KEY"); - - public static final String BILTHON_5_BRAIN_KEY = System.getenv("BILTHON_5_BRAIN_KEY"); - - public static final String BILTHON_7_BRAIN_KEY = System.getenv("BILTHON_7_BRAIN_KEY"); - - public static final String BIP39_KEY = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; - - public static final String WIF = "5KMzB2GqGhnh7ufhgddmz1eKPHS72uTLeL9hHjSvPb1UywWknF5"; - - public static final String BILTHON_83_PASSWORD = System.getenv("BILTHON_83_PASSWORD"); - - public static final String BILTHON_25_PASSWORD = System.getenv("BILTHON_25_PASSWORD"); - - public static final String BILTHON_11_BRAIN_KEY = System.getenv("BILTHON_11_BRAINKEY"); - - public static final String BILTHON_15_BRAIN_KEY = System.getenv("BILTHON_15_BRAINKEY"); - - public static final String BILTHON_16_BRAIN_KEY = System.getenv("BILTHON_16_BRAINKEY"); - - public static final String BILTHON_36_BRAIN_KEY = System.getenv("BILTHON_36_BRAINKEY"); - - public static final String GENERIC_PASSWORD = System.getenv("GENERIC_PASSWORD"); - - public static final String DISCLOSABLE_PASSWORD = System.getenv("DISCLOSABLE_PASSWORD"); - - // Static block information used for transaction serialization tests - public static int REF_BLOCK_NUM = 56204; - public static int REF_BLOCK_PREFIX = 1614747814; - public static int RELATIVE_EXPIRATION = 1478385607; - - public static void main(String[] args) { - Test test = new Test(); - -// test.testTransactionSerialization(); -// ECKey.ECDSASignature signature = test.testSigning(); - -// try { -// test.testWebSocketTransfer(); -// } catch (IOException e) { -// e.printStackTrace(); -// } -// test.testCustomSerializer(); -// test.testUserAccountSerialization(); -// test.testTransactionSerialization(); -// test.testLoginSerialization(); -// test.testNetworkBroadcastSerialization(); -// test.testNetworkBroadcastDeserialization(); -// test.testGetDynamicParams(); -// test.testGetRequiredFeesSerialization(); -// test.testRequiredFeesResponse(); -// test.testTransactionBroadcastSequence(); -// test.testAccountLookupDeserialization(); -// test.testPrivateKeyManipulations(); -// test.testPublicKeyManipulations(); -// test.testGetAccountByName(); -// test.testGetRequiredFees(); -// test.testRandomNumberGeneration(); -// test.testBrainKeyOperations(false); -// test.testBip39Opertion(); -// test.testAccountNamebyAddress(); -// test.testAccountNameById(); -// test.testRelativeAccountHistory(); -// test.testingInvoiceGeneration(); -// test.testCompression(); -// test.testAccountUpdateSerialization(); -// test.testAccountUpdateOperationBroadcast(); -// test.testCreateBinFile(); -// test.testImportBinFile(); -// test.testExportBinFile(); -// test.testLzmaCompression(); -// test.testLzmaDecompression(); -// test.testSimpleDecompression(); -// test.testLookupAccounts(); -// test.testLookupAccounts(); -// test.testDecodeMemo(); -// test.testGetRelativeAccountHistory(); -// test.testLookupAssetSymbols(); -// test.testListAssets(); -// test.testGetObjects(); -// test.testGetBlockHeader(); - test.testGetLimitOrders(); -// test.testGetTradeHistory(); -// test.testAssetSerialization(); -// test.testGetMarketHistory(); -// test.testGetAccountBalances(); -// test.testGetAssetHoldersCount(); -// test.testSubscription(null); - } -} diff --git a/app/src/main/java/de/bitsharesmunich/graphenej/Test.java b/app/src/main/java/de/bitsharesmunich/graphenej/Test.java deleted file mode 100644 index 962d841..0000000 --- a/app/src/main/java/de/bitsharesmunich/graphenej/Test.java +++ /dev/null @@ -1,1423 +0,0 @@ -package de.bitsharesmunich.graphenej; - -import com.google.common.primitives.Bytes; -import de.bitsharesmunich.graphenej.errors.MalformedOperationException; -import de.bitsharesmunich.graphenej.interfaces.SubscriptionListener; -import de.bitsharesmunich.graphenej.models.*; -import de.bitsharesmunich.graphenej.models.backup.LinkedAccount; -import de.bitsharesmunich.graphenej.models.backup.PrivateKeyBackup; -import de.bitsharesmunich.graphenej.models.backup.Wallet; -import de.bitsharesmunich.graphenej.models.backup.WalletBackup; -import de.bitsharesmunich.graphenej.objects.Memo; -import com.google.common.primitives.UnsignedLong; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonArray; -import com.google.gson.reflect.TypeToken; -import de.bitsharesmunich.graphenej.errors.MalformedAddressException; -import de.bitsharesmunich.graphenej.errors.MalformedTransactionException; -import de.bitsharesmunich.graphenej.interfaces.WitnessResponseListener; -import de.bitsharesmunich.graphenej.operations.AccountUpdateOperation; -import de.bitsharesmunich.graphenej.operations.AccountUpdateOperationBuilder; -import de.bitsharesmunich.graphenej.operations.TransferOperation; -import de.bitsharesmunich.graphenej.operations.TransferOperationBuilder; -import de.bitsharesmunich.graphenej.test.NaiveSSLContext; -import com.neovisionaries.ws.client.*; -import de.bitsharesmunich.graphenej.api.*; -import org.bitcoinj.core.*; -import org.spongycastle.crypto.digests.RIPEMD160Digest; -import org.tukaani.xz.*; - -import javax.net.ssl.SSLContext; -import java.io.*; -import java.lang.reflect.Type; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.*; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Created by nelson on 11/9/16. - */ -public class Test { - - public static final String AMAZON_WITNESS = "ws://54.91.97.99:8090"; - public static final String WITNESS_URL = "api://api.devling.xyz:8088"; - public static final String OPENLEDGER_WITNESS_URL = "wss://bitshares.openledger.info/api"; - public static final String BLOCK_PAY_DE = "wss://de.blockpay.ch/node"; - public static final String BLOCK_PAY_FR = "wss://fr.blockpay.ch/node"; - - private Transaction transaction; - - public Transaction GetTransaction() { - return transaction; - } - - private WitnessResponseListener mListener = new WitnessResponseListener() { - - @Override - public void onSuccess(WitnessResponse response) { - - if (response.result.getClass() == AccountProperties.class) { - AccountProperties accountProperties = (AccountProperties) response.result; - System.out.println("Got account properties"); - System.out.println("account: " + accountProperties.toString()); - System.out.println("id: " + accountProperties.id); - - } else if (response.result.getClass() == ArrayList.class) { - List list = (List) response.result; - if (list.size() > 0) { - if (list.get(0) instanceof AccountProperties) { - List accountPropertiesList = list; - for (AccountProperties accountProperties : accountPropertiesList) { - System.out.println("Account id: " + accountProperties.id); - } - } else if (list.get(0) instanceof AssetAmount) { - AssetAmount assetAmount = (AssetAmount) list.get(0); - System.out.println("Got fee"); - System.out.println("amount: " + assetAmount.getAmount() + ", asset id: " + assetAmount.getAsset().getObjectId()); - } else if (list.get(0).getClass() == ArrayList.class) { - List sl = (List) list.get(0); - if (sl.size() > 0) { - if (response.result.getClass() == AccountProperties.class) { - AccountProperties accountProperties = (AccountProperties) response.result; - System.out.println("Got account properties " + accountProperties); - } else { - String accountId = (String) sl.get(0); - System.out.println("account id : " + accountId); - 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); - ArrayList userAccounts = new ArrayList(); - userAccounts.add(new UserAccount(accountId)); - mWebSocket.addListener(new GetAccounts(userAccounts, null)); - mWebSocket.connect(); - } catch (IOException e) { - System.out.println("IOException. Msg: " + e.getMessage()); - } catch (WebSocketException e) { - System.out.println("WebSocketException. Msg: " + e.getMessage()); - } catch (NoSuchAlgorithmException ex) { - Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex); - } - } - } - } - } else { - System.out.println("Got empty list!"); - } - } else if (response.result.getClass() == JsonArray.class) { - System.out.println("Json array : " + ((JsonArray) response.result)); - } else { - System.out.println("Got other: " + response.result.getClass()); - } - } - - @Override - public void onError(BaseResponse.Error error) { - System.out.println("onError. message: " + error.message); - } - }; - - public ECKey.ECDSASignature testSigning() { - byte[] serializedTransaction = this.transaction.toBytes(); - Sha256Hash hash = Sha256Hash.wrap(Sha256Hash.hash(serializedTransaction)); - byte[] bytesDigest = hash.getBytes(); - ECKey sk = transaction.getPrivateKey(); - ECKey.ECDSASignature signature = sk.sign(hash); - return signature; - } - - public String testSigningMessage() { - byte[] serializedTransaction = this.transaction.toBytes(); - Sha256Hash hash = Sha256Hash.wrap(Sha256Hash.hash(serializedTransaction)); - ECKey sk = transaction.getPrivateKey(); - return sk.signMessage(hash.toString()); - } - - public byte[] signMessage() { - byte[] serializedTransaction = this.transaction.toBytes(); - Sha256Hash hash = Sha256Hash.wrap(Sha256Hash.hash(serializedTransaction)); - System.out.println(">> digest <<"); - System.out.println(Util.bytesToHex(hash.getBytes())); - ECKey sk = transaction.getPrivateKey(); - System.out.println("Private key bytes"); - System.out.println(Util.bytesToHex(sk.getPrivKeyBytes())); - boolean isCanonical = false; - int recId = -1; - ECKey.ECDSASignature sig = null; - while (!isCanonical) { - sig = sk.sign(hash); - if (!sig.isCanonical()) { - System.out.println("Signature was not canonical, retrying"); - continue; - } else { - System.out.println("Signature is canonical"); - isCanonical = true; - } - // Now we have to work backwards to figure out the recId needed to recover the signature. - for (int i = 0; i < 4; i++) { - ECKey k = ECKey.recoverFromSignature(i, sig, hash, sk.isCompressed()); - if (k != null && k.getPubKeyPoint().equals(sk.getPubKeyPoint())) { - recId = i; - break; - } else { - if (k == null) { - System.out.println("Recovered key was null"); - } - if (k.getPubKeyPoint().equals(sk.getPubKeyPoint())) { - System.out.println("Recovered pub point is not equal to sk pub point"); - } - } - } - if (recId == -1) { - throw new RuntimeException("Could not construct a recoverable key. This should never happen."); - } - } - int headerByte = recId + 27 + (sk.isCompressed() ? 4 : 0); - byte[] sigData = new byte[65]; // 1 header + 32 bytes for R + 32 bytes for S - sigData[0] = (byte) headerByte; - System.arraycopy(Utils.bigIntegerToBytes(sig.r, 32), 0, sigData, 1, 32); - System.arraycopy(Utils.bigIntegerToBytes(sig.s, 32), 0, sigData, 33, 32); - System.out.println("recId: " + recId); - System.out.println("r: " + Util.bytesToHex(sig.r.toByteArray())); - System.out.println("s: " + Util.bytesToHex(sig.s.toByteArray())); - return sigData; -// return new String(Base64.encode(sigData), Charset.forName("UTF-8")); - } - - public void testTransactionSerialization(long head_block_number, String head_block_id, long relative_expiration) { - BlockData blockData = new BlockData(head_block_number, head_block_id, relative_expiration); - - ArrayList operations = new ArrayList(); - UserAccount from = new UserAccount("1.2.138632"); - UserAccount to = new UserAccount("1.2.129848"); - AssetAmount amount = new AssetAmount(UnsignedLong.valueOf(100), new Asset("1.3.120")); - AssetAmount fee = new AssetAmount(UnsignedLong.valueOf(264174), new Asset("1.3.0")); - operations.add(new TransferOperation(from, to, amount, fee)); - BrainKey brainKey = new BrainKey(Main.BILTHON_83_BRAIN_KEY, 0); - this.transaction = new Transaction(brainKey.getWalletImportFormat(), blockData, operations); - byte[] serializedTransaction = this.transaction.toBytes(); - System.out.println("Serialized transaction"); - System.out.println(Util.bytesToHex(serializedTransaction)); - } - - public void testCustomSerializer() { - AssetAmount amount = new AssetAmount(UnsignedLong.valueOf(100), new Asset("1.3.120")); - String jsonAmount = amount.toJsonString(); - System.out.println("JSON amount"); - System.out.println(jsonAmount); - } - - public void testUserAccountSerialization() { - UserAccount account = new UserAccount("1.2.143563"); - System.out.println(Util.bytesToHex(account.toBytes())); - } - - public void testLoginSerialization() { - ArrayList loginParams = new ArrayList<>(); -// loginParams.add("nelson"); -// loginParams.add("supersecret"); - loginParams.add(null); - loginParams.add(null); - ApiCall loginCall = new ApiCall(1, "login", loginParams, "2.0", 1); - String jsonLoginCall = loginCall.toJsonString(); - System.out.println("login call"); - System.out.println(jsonLoginCall); - } - - public void testNetworkBroadcastSerialization() { - ArrayList params = new ArrayList<>(); - ApiCall networkParamsCall = new ApiCall(3, "network_broadcast", params, "2.0", 1); - String call = networkParamsCall.toJsonString(); - System.out.println("network broadcast"); - System.out.println(call); - } - - public void testNetworkBroadcastDeserialization() { - String response = "{\"id\":2,\"result\":2}"; - Gson gson = new Gson(); - Type ApiIdResponse = new TypeToken>() { - }.getType(); - WitnessResponse witnessResponse = gson.fromJson(response, ApiIdResponse); - } - - public void testGetDynamicParams() { - ArrayList emptyParams = new ArrayList<>(); - ApiCall getDynamicParametersCall = new ApiCall(0, "get_dynamic_global_properties", emptyParams, "2.0", 0); - System.out.println(getDynamicParametersCall.toJsonString()); - } - - public void testRequiredFeesResponse() { - String response = "{\"id\":1,\"result\":[{\"amount\":264174,\"asset_id\":\"1.3.0\"}]}"; - Type AccountLookupResponse = new TypeToken>>() { - }.getType(); - GsonBuilder gsonBuilder = new GsonBuilder(); - gsonBuilder.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer()); - WitnessResponse> witnessResponse = gsonBuilder.create().fromJson(response, AccountLookupResponse); - for (AssetAmount assetAmount : witnessResponse.result) { - System.out.println("asset : " + assetAmount.toJsonString()); - } - } - - public void testTransactionBroadcastSequence() { - WitnessResponseListener listener = new WitnessResponseListener() { - @Override - public void onSuccess(WitnessResponse response) { - System.out.println("onSuccess"); - System.out.println("Callback.Thread. name: "+Thread.currentThread().getName()+", id: "+Thread.currentThread().getId()); - } - - @Override - public void onError(BaseResponse.Error error) { - System.out.println("onError"); - System.out.println(error.data.message); - System.out.println("Callback.Thread. name: "+Thread.currentThread().getName()+", id: "+Thread.currentThread().getId()); - } - }; - - try { - ECKey sourcePrivateKey = new BrainKey(Main.BILTHON_15_BRAIN_KEY, 0).getPrivateKey(); - PublicKey to1 = new PublicKey(ECKey.fromPublicOnly(new BrainKey(Main.BILTHON_5_BRAIN_KEY, 0).getPublicKey())); - PublicKey to2 = new PublicKey(ECKey.fromPublicOnly(new BrainKey(Main.BILTHON_16_BRAIN_KEY, 0).getPublicKey())); - - // Creating memo - long nonce = 1; - byte[] encryptedMessage = Memo.encryptMessage(sourcePrivateKey, to1, nonce, "another message"); - Memo memo = new Memo(new Address(ECKey.fromPublicOnly(sourcePrivateKey.getPubKey())), new Address(to1.getKey()), nonce, encryptedMessage); - - // Creating operation 1 - TransferOperation transferOperation1 = new TransferOperationBuilder() - .setTransferAmount(new AssetAmount(UnsignedLong.valueOf(1), new Asset("1.3.0"))) - .setSource(new UserAccount("1.2.143563")) // bilthon-15 - .setDestination(new UserAccount("1.2.139313")) // bilthon-5 - .setFee(new AssetAmount(UnsignedLong.valueOf(264174), new Asset("1.3.0"))) - .build(); - - // Creating operation 2 - TransferOperation transferOperation2 = new TransferOperationBuilder() - .setTransferAmount(new AssetAmount(UnsignedLong.valueOf(1), new Asset("1.3.0"))) - .setSource(new UserAccount("1.2.143563")) // bilthon-15 - .setDestination(new UserAccount("1.2.143569")) // bilthon-16 - .setFee(new AssetAmount(UnsignedLong.valueOf(264174), new Asset("1.3.0"))) - .build(); - - - // Adding operations to the operation list - ArrayList operationList = new ArrayList<>(); - operationList.add(transferOperation1); - operationList.add(transferOperation2); - - Transaction transaction = new Transaction(sourcePrivateKey, 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(BLOCK_PAY_DE); - - mWebSocket.addListener(new TransactionBroadcastSequence(transaction, new Asset("1.3.0"), listener)); - mWebSocket.connect(); - System.out.println("Main.Thread. name: "+Thread.currentThread().getName()+", id: "+Thread.currentThread().getId()); - } catch (MalformedOperationException e) { - System.out.println("MalformedTransactionException. Msg: " + e.getMessage()); - } 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("NoSuchAlgoritmException. Msg: " + e.getMessage()); - } - } - - public void testAccountLookupDeserialization() { - String response = "{\"id\":1,\"result\":[[\"ken\",\"1.2.3111\"],[\"ken-1\",\"1.2.101491\"],[\"ken-k\",\"1.2.108646\"]]}"; - Type AccountLookupResponse = new TypeToken>>>() { - }.getType(); - Gson gson = new Gson(); - WitnessResponse>> witnessResponse = gson.fromJson(response, AccountLookupResponse); - for (int i = 0; i < witnessResponse.result.size(); i++) { - System.out.println("suggested name: " + witnessResponse.result.get(i).get(0)); - } - } - - public void testPrivateKeyManipulations() { - String brainKeyWords = Main.BILTHON_15_BRAIN_KEY; - - BrainKey brainKey = new BrainKey(brainKeyWords, 0); - - ECKey privateKey = DumpedPrivateKey.fromBase58(null, brainKey.getWalletImportFormat()).getKey(); - Address address = new Address(ECKey.fromPublicOnly(privateKey.getPubKey())); - System.out.println("Raw private key..........: " + Util.bytesToHex(privateKey.getSecretBytes())); - System.out.println("WIF private key..........: " + brainKey.getWalletImportFormat()); - System.out.println("Public key uncompressed..: " + Util.bytesToHex(privateKey.getPubKey())); - System.out.println("Public key compressed....: " + Util.bytesToHex(privateKey.getPubKeyPoint().getEncoded(true))); - System.out.println("Address..................: " + address.toString()); - } - - public void testPublicKeyManipulations() { -// PublicKey publicKey = new PublicKey("BTS8RiFgs8HkcVPVobHLKEv6yL3iXcC9SWjbPVS15dDAXLG9GYhnY"); -// System.out.println("Public key bytes"); -// System.out.println(Util.bytesToHex(publicKey.toBytes())); - Address address = null; - try { - address = new Address("BTS8RiFgs8HkcVPVobHLKEv6yL3iXcC9SWjbPVS15dDAXLG9GYhnY"); - System.out.println("Public key"); - System.out.println(Util.bytesToHex(address.getPublicKey().toBytes())); - } catch (MalformedAddressException e) { - e.printStackTrace(); - } - } - - public void testGetAccountByName() { - - WitnessResponseListener accountByNameListener = new WitnessResponseListener() { - @Override - public void onSuccess(WitnessResponse response) { - System.out.println("onSuccess"); - AccountProperties accountProperties = (AccountProperties) response.result; - System.out.println("Owner and active authorities are the same: "+accountProperties.active.equals(accountProperties.owner)); - } - - @Override - public void onError(BaseResponse.Error error) { - System.out.println("onError. Msg: "+error.message); - } - }; - - try { - SSLContext context = null; - context = NaiveSSLContext.getInstance("TLS"); - WebSocketFactory factory = new WebSocketFactory(); - - // Set the custom SSL context. - factory.setSSLContext(context); - - WebSocket mWebSocket = factory.createSocket(BLOCK_PAY_DE); - - mWebSocket.addListener(new GetAccountByName("bilthon-83", accountByNameListener)); - 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()); - } - } - - public void testGetRequiredFees() { - ArrayList accountParams = new ArrayList<>(); - Asset asset = new Asset("1.3.0"); - UserAccount from = new UserAccount("1.2.138632"); - UserAccount to = new UserAccount("1.2.129848"); - AssetAmount amount = new AssetAmount(UnsignedLong.valueOf(100), new Asset("1.3.120")); - AssetAmount fee = new AssetAmount(UnsignedLong.valueOf(264174), new Asset("1.3.0")); - TransferOperation transfer = new TransferOperation(from, to, amount, fee); - ArrayList operations = new ArrayList<>(); - operations.add(transfer); - - accountParams.add(operations); - accountParams.add(asset.getObjectId()); - - try { - WebSocketFactory factory = new WebSocketFactory().setConnectionTimeout(5000); - WebSocket mWebSocket = factory.createSocket(WITNESS_URL); - mWebSocket.addListener(new GetRequiredFees(operations, asset, mListener)); - mWebSocket.connect(); - } catch (IOException e) { - System.out.println("IOException. Msg: " + e.getMessage()); - } catch (WebSocketException e) { - System.out.println("WebSocketException. Msg: " + e.getMessage()); - } - } - - /** - * The final purpose of this test is to convert the plain brainkey at - * Main.BILTHON_83_BRAIN_KEY into the WIF at Main.WIF - */ - public void testBrainKeyOperations(boolean random) { - try { - BrainKey brainKey; - if (random) { - String current = new java.io.File(".").getCanonicalPath(); - File file = new File(current + "/src/main/java/com/luminiasoft/bitshares/brainkeydict.txt"); - - BufferedReader bufferedReader = new BufferedReader(new FileReader(file)); - StringBuffer buffer = new StringBuffer(); - String words = bufferedReader.readLine(); - String suggestion = BrainKey.suggest(words); - brainKey = new BrainKey(suggestion, 0); - } else { - System.out.println("Using brain key: " + Main.BILTHON_5_BRAIN_KEY); - brainKey = new BrainKey(Main.BILTHON_5_BRAIN_KEY, 0); - - } - ECKey key = brainKey.getPrivateKey(); - System.out.println("Private key..................: " + Util.bytesToHex(key.getSecretBytes())); - String wif = key.getPrivateKeyAsWiF(NetworkParameters.fromID(NetworkParameters.ID_MAINNET)); - System.out.println("Wif Compressed...............: " + wif); - String wif2 = key.decompress().getPrivateKeyAsWiF(NetworkParameters.fromID(NetworkParameters.ID_MAINNET)); - System.out.println("Wif Decompressed.............: " + wif2); - System.out.println("Wif from BrainKey............: " + brainKey.getWalletImportFormat()); - - byte[] uncompressedPubKey = key.decompress().getPubKey(); - byte[] compressedPubKey = key.getPubKey(); - - System.out.println("Public Key Decompressed......: " + Util.bytesToHex(uncompressedPubKey)); - System.out.println("Public Key Compressed........: " + Util.bytesToHex(compressedPubKey)); - - // Address generation test - Address address = new Address(ECKey.fromPublicOnly(key.getPubKey())); - System.out.println("Block explorer's address.....: " + address); - } catch (FileNotFoundException e) { - System.out.println("FileNotFoundException. Msg: " + e.getMessage()); - } catch (IOException e) { - System.out.println("IOException. Msg: " + e.getMessage()); - } - } - - public byte[] calculateChecksum(byte[] input) { - byte[] answer = new byte[4]; - RIPEMD160Digest ripemd160Digest = new RIPEMD160Digest(); - ripemd160Digest.update(input, 0, input.length); - byte[] output = new byte[160 / 8]; - ripemd160Digest.doFinal(output, 0); - System.arraycopy(output, 0, answer, 0, 4); - return answer; - } - - public void testBip39Opertion() { - BIP39 bip39 = new BIP39(Main.BIP39_KEY, ""); - } - - public void testAccountNamebyAddress() { - WitnessResponseListener listener = new WitnessResponseListener() { - @Override - public void onSuccess(WitnessResponse response) { - System.out.println("onSuccess"); - } - - @Override - public void onError(BaseResponse.Error error) { - System.out.println("onError"); - } - }; - - BrainKey brainKey = new BrainKey(Main.BILTHON_83_ORIGINAL_BRAIN_KEY, 0); -// Address address = new Address(ECKey.fromPublicOnly(brainKey.getPrivateKey().getPubKey())); - try { - Address address = new Address("BTS5BgjNRDeawGSc1NPk91p2BYYEhJWKgsjLZGDmFgY6uwhAYLy9G"); - - // 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(BLOCK_PAY_DE); - mWebSocket.addListener(new GetAccountsByAddress(address, listener)); - 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()); - } - catch (MalformedAddressException e) { - System.out.println("MalformedAddressException. Msg: "+e.getMessage()); - } - } - - public void testAccountNameById() { - WitnessResponseListener listener = new WitnessResponseListener() { - @Override - public void onSuccess(WitnessResponse response) { - System.out.println("onSuccess"); - } - - @Override - public void onError(BaseResponse.Error error) { - System.out.println("onError"); - } - }; - - 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(BLOCK_PAY_FR); - ArrayList userAccounts = new ArrayList<>(); - userAccounts.add(new UserAccount("1.2.138632")); - mWebSocket.addListener(new GetAccounts(userAccounts, listener)); - 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()); - } - } - - 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(AMAZON_WITNESS); - 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()); - } - } - - public void testingInvoiceGeneration() { - LineItem[] lineItem = new LineItem[]{new LineItem("Apples", 2, 2.00)}; - Invoice invoice = new Invoice("bilthon-83", "Bilthon's store", "Invoice #12", "BTS", lineItem, "Thank you", ""); - String qrCodeData = Invoice.toQrCode(invoice); - System.out.println("qrCodeData"); - System.out.println(qrCodeData); - Invoice recovered = Invoice.fromQrCode(qrCodeData); - System.out.println("recovered invoice: " + recovered.toJsonString()); - } - - public void testCompression() { - String test = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; - System.out.println("to compress"); - System.out.println(Util.bytesToHex(test.getBytes())); - byte[] compressed = Util.compress(test.getBytes(), Util.XZ); - System.out.println("compressed"); - System.out.println(Util.bytesToHex(compressed)); - } - - public void testCreateBinFile() { - byte[] fileOutput = FileBin.getBytesFromBrainKey(Main.BILTHON_83_BRAIN_KEY, "123456", "bithon-83"); - ///String stringFile = "02f9f3eb0f61a0a96134975d86048bf92e114d6a1ce286140cad3a96c33e697282bc0a8a24d1ad0c7bc084a79816ce38e36bd2d624aa8bf686f53fb4c7e25e3974da9b40e0b17e9d0b5b82793a04b19646169c49c58cd67f4950aee7d275141dd24f52baaaee772995a9bd6a6562a7a38aae08951236d3f612aecef7aedd720a91eacbab3a792ca3ebe0105838fe11f6e9d0e83e5d77eb82f17c7ba85c670e69294a8bcf8365cfeca487a60093498496bbec394c729e3fda9f32fdccdea56288b36fb14a26aa309b548a6dd9c1d616d22167348f8d580f9dc7361b4457d2dc6d75ec985d8e2d3dcdff89cd425d9f14037ac961eb10ac5f92bab356ccecd8cf018ec05ab40d915b628a75ae32cfa4005634f08b24c0dc8c5a7636ed70cbd86a7f0c4f6236d74310470fafe3af8b5346c8cb61957f7292b468d276498f9e806399588b0afd5777e6ee5fe7cd3a6691d9b5486cb5c7adbd5ad0b17588dd32d82b01d49ecf0f2bf24ee54a490ee620e8ab049047ffa416b5efa8f1f0155d8f1be866a10d0d62ae44a3a8ecc0121c08837c2ee1a25f8b6dd7266273c41f4b9a5e3d600e3fb4de870f99ab1a7196d93f222595f92e97a2480f58b61b62639154a374b987664fd317622aaad156f831b03f2d9606537b65b3b1fcfb1fb6be39560ad2c301dd1fc25cee755e61b49ebfe42ca7e64b4b0fc4aa347b48a85c0b585a3499fe278e25cb2141f8009b9afc875fa2a2c439bf6cdec4b5190a6deb7f9390f072beb24749a8a2114cc1870c07be079abb3ee0ebc827f9b53e158a529bc6552eba280f05edf5f7ae1911de7acb4888150a509d029ec7c9da6de8adabbca6773a0a293a0a42de8278c82e88b9390b42b56f58bd8633fb97130e799a47a744e2e8958fd5"; - //fileOutput = new BigInteger(stringFile, 16).toByteArray(); - System.out.println(FileBin.getBrainkeyFromByte(fileOutput, "123456")); - } - - public void testImportBinFile() { - String password = Main.GENERIC_PASSWORD; - try { - String current = new File(".").getCanonicalPath(); - File file = new File(current + "/bilthon-36.bin"); - Path path = Paths.get(file.getAbsolutePath()); - byte[] data = Files.readAllBytes(path); - byte[] publicKey = new byte[FileBin.PUBLIC_KEY_LENGTH]; - System.arraycopy(data, 0, publicKey, 0, FileBin.PUBLIC_KEY_LENGTH); - - MessageDigest md = MessageDigest.getInstance("SHA-256"); - ECKey randomECKey = ECKey.fromPublicOnly(publicKey); - byte[] finalKey = randomECKey.getPubKeyPoint().multiply(ECKey.fromPrivate(md.digest(password.getBytes("UTF-8"))).getPrivKey()).normalize().getXCoord().getEncoded(); - System.out.println("final key : "+Util.bytesToHex(finalKey)); - Address address = new Address(ECKey.fromPublicOnly(ECKey.fromPrivate(finalKey).getPubKey())); - System.out.println("address : "+address.toString()); - - WalletBackup walletBackup = FileBin.deserializeWalletBackup(data, password); - String brainKeyString = walletBackup.getWallet(0).decryptBrainKey(password); - System.out.println("Brain key: "+brainKeyString); - BrainKey brainKey = new BrainKey(brainKeyString, 1); - byte[] brainKeyDerivedPrivateKey = brainKey.getPrivateKey().getPrivKeyBytes(); - System.out.println("Brainkey derived private....: " + Util.bytesToHex(brainKeyDerivedPrivateKey)); - - byte[] encryptionKey = walletBackup.getWallet(0).getEncryptionKey(password); - byte[] privateKey0 = walletBackup.getPrivateKeyBackup(0).decryptPrivateKey(encryptionKey); - byte[] privateKey1 = walletBackup.getPrivateKeyBackup(1).decryptPrivateKey(encryptionKey); - System.out.println("Encrypted private key 1.....: "+Util.bytesToHex(privateKey0)); - System.out.println("Encrypted private key 2.....: "+Util.bytesToHex(privateKey1)); - - Address addr1 = new Address(ECKey.fromPublicOnly(ECKey.fromPrivate(brainKeyDerivedPrivateKey).getPubKey())); - Address addr2 = new Address(ECKey.fromPublicOnly(ECKey.fromPrivate(privateKey0).getPubKey())); - Address addr3 = new Address(ECKey.fromPublicOnly(ECKey.fromPrivate(privateKey1).getPubKey())); - System.out.println("Addr1: "+addr1.toString()); - System.out.println("Addr2: "+addr2.toString()); - System.out.println("Addr3: "+addr3.toString()); - } catch (IOException e) { - System.out.println("IOException while trying to open bin file. Msg: "+e.getMessage()); - } catch (NoSuchAlgorithmException e) { - System.out.println("NoSuchAlgorithmException while trying to open bin file. Msg: "+e.getMessage()); - } - } - - public void testExportBinFile(){ - String password = Main.DISCLOSABLE_PASSWORD; - BrainKey brainKey = new BrainKey(Main.BILTHON_36_BRAIN_KEY, 0); - String accountName = "bilthon-36"; - Wallet wallet = new Wallet(accountName, brainKey.getBrainKey(), brainKey.getSequenceNumber(), Chains.BITSHARES.CHAIN_ID, password); - byte[] privateKey = brainKey.getPrivateKey().getPrivKeyBytes(); - PrivateKeyBackup privateKeyBackup = new PrivateKeyBackup(privateKey, brainKey.getSequenceNumber(), 1, wallet.getEncryptionKey(password)); - LinkedAccount linkedAccount = new LinkedAccount(accountName, Chains.BITSHARES.CHAIN_ID); - - ArrayList walletList = new ArrayList<>(); - walletList.add(wallet); - ArrayList keyList = new ArrayList<>(); - keyList.add(privateKeyBackup); - ArrayList linkedAccounts = new ArrayList<>(); - linkedAccounts.add(linkedAccount); - WalletBackup backup = new WalletBackup(walletList, keyList, linkedAccounts); - byte[] serialized = FileBin.serializeWalletBackup(backup, password); - System.out.println("Serialized: "+Util.bytesToHex(serialized)); - try { - String current = new File(".").getCanonicalPath(); - String fullPath = current + "/scwall_"+accountName+".bin"; - System.out.println("Full path: "+fullPath); - File file = new File(fullPath); - FileOutputStream out = new FileOutputStream(file); - out.write(serialized); - } catch (IOException e) { - e.printStackTrace(); - } - } - - public void testLzmaCompression(){ - String data = "A long time ago in a galaxy far, far away..."; - byte[] compressed = Util.compress(data.getBytes(), Util.LZMA); - - try { - String current = new File(".").getCanonicalPath(); - File file = new File(current + "/src/main/java/de/bitsharesmunich/graphenej/java_compressed_1.4.lzma"); - FileOutputStream out = new FileOutputStream(file); - System.out.println("Writing "+compressed.length+" bytes"); - out.write(compressed); - out.close(); - }catch(IOException e){ - System.out.println("IOException. Msg: "+e.getMessage()); - } - } - - public void testSimpleDecompression(){ - try{ - String current = new File(".").getCanonicalPath(); -// File file = new File(current + "/src/main/java/de/bitsharesmunich/graphenej/node_compressed_1.2.lzma"); - File file = new File(current + "/src/main/java/de/bitsharesmunich/graphenej/decrypted.bin"); - Path path = Paths.get(file.getAbsolutePath()); - byte[] data = Files.readAllBytes(path); - byte[] decompressed = Util.decompress(data, Util.LZMA); - System.out.println("Decompressed.......: "+Util.bytesToHex(decompressed)); - String message = new String(decompressed); - System.out.println("Decompressed msg...: "+message); - } catch (IOException e) { - System.out.println("IOException. Msg: "+e.getMessage()); - } - } - - public void testLzmaDecompression(){ - try { - String current = new File(".").getCanonicalPath(); - File file = new File(current + "/src/main/java/de/bitsharesmunich/graphenej/java_compressed_1.4.lzma"); - Path path = Paths.get(file.getAbsolutePath()); - byte[] data = Files.readAllBytes(path); - System.out.println("Compressed bytes...: " + Util.bytesToHex(data)); - - InputStream in = null; - byte[] decompressed; - byte[] properties = Arrays.copyOfRange(data, 0, 1); - byte[] dictSize = Arrays.copyOfRange(data, 1, 5); - byte[] uncompressedSize = Arrays.copyOfRange(data, 5, 13); - byte[] header = Bytes.concat(properties, Util.revertBytes(dictSize), Util.revertBytes(uncompressedSize)); - byte[] payload = Arrays.copyOfRange(data, 13, data.length); - System.out.println("Header.............: "+Util.bytesToHex(header)); - System.out.println("Payload............: "+Util.bytesToHex(payload)); - ByteArrayInputStream input = new ByteArrayInputStream(Bytes.concat(header, payload)); - ByteArrayOutputStream output = new ByteArrayOutputStream(2 * 2048); -// in = new LZMAInputStream(input, 44, (byte) 0x5d, 65536); - in = new LZMAInputStream(input); - int size; - try{ - while ((size = in.read()) != -1) { - output.write(size); - } - }catch(IOException e){ - System.out.println("IOException detected. End of stream reached. Msg: "+e.getMessage()); - } - in.close(); - decompressed = output.toByteArray(); - - System.out.println("Decompressed bytes.: " + Util.bytesToHex(decompressed)); - String decompressedString = new String(decompressed); - System.out.println("Decompressed: " + decompressedString); - } catch (CorruptedInputException e) { - System.out.println("CorruptedInputException. Msg: " + e.getMessage()); - } catch (UnsupportedOptionsException e){ - System.out.println("UnsupportedOptionsException. Msg: "+e.getMessage()); - } catch(IOException e){ - System.out.println("IOException. Msg: "+e.getMessage()); - } - } - - public void testAccountUpdateSerialization() { - String newAddress = "BTS8RiFgs8HkcVPVobHLKEv6yL3iXcC9SWjbPVS15dDAXLG9GYhnY"; - try { - Address address = new Address(newAddress); - HashMap authMap = new HashMap<>(); - authMap.put(address.getPublicKey(), 1); - Authority authority = new Authority(1, authMap, null); - AccountOptions options = new AccountOptions(address.getPublicKey()); - BrainKey brainKey = new BrainKey(Main.BILTHON_83_BRAIN_KEY, 0); - - AccountUpdateOperation operation = new AccountUpdateOperationBuilder() - .setAccount(new UserAccount("1.2.140994")) - .setActive(authority) - .setOptions(options) - .build(); - - ArrayList opList = new ArrayList<>(); - opList.add(operation); - - BlockData blockData = new BlockData(Main.REF_BLOCK_NUM, Main.REF_BLOCK_PREFIX, Main.RELATIVE_EXPIRATION); - ECKey privateKey = brainKey.getPrivateKey(); - Transaction transaction = new Transaction(privateKey, blockData, opList); - - System.out.println("Json object"); - System.out.println(transaction.toJsonString()); - System.out.println("Serialized transaction"); - System.out.println(Util.bytesToHex(transaction.toBytes())); - } catch (MalformedAddressException e) { - System.out.println("MalformedAddressException. Msg: " + e.getMessage()); - } catch (MalformedOperationException e) { - System.out.println("MalformedTransactionException. Msg: " + e.getMessage()); - } - } - - public void testAccountUpdateOperationBroadcast() { - - WitnessResponseListener listener = new WitnessResponseListener() { - @Override - public void onSuccess(WitnessResponse response) { - System.out.println("onSuccess"); - } - - @Override - public void onError(BaseResponse.Error error) { - System.out.println("onError"); - } - }; - - String newAddress = "BTS8RiFgs8HkcVPVobHLKEv6yL3iXcC9SWjbPVS15dDAXLG9GYhnY"; - try { - Address address = new Address(newAddress); - HashMap authMap = new HashMap<>(); - authMap.put(address.getPublicKey(), 1); - Authority authority = new Authority(1, authMap, null); - AccountOptions options = new AccountOptions(address.getPublicKey()); - BrainKey brainKey = new BrainKey(Main.BILTHON_83_BRAIN_KEY, 0); - Transaction transaction = new AccountUpdateTransactionBuilder(brainKey.getPrivateKey()) - .setAccont(new UserAccount("1.2.138632")) -// .setOwner(authority) - .setActive(authority) - .setOptions(options) - .build(); - - SSLContext context = null; - context = NaiveSSLContext.getInstance("TLS"); - WebSocketFactory factory = new WebSocketFactory(); - - // Set the custom SSL context. - factory.setSSLContext(context); - - WebSocket mWebSocket = factory.createSocket(BLOCK_PAY_DE); - - mWebSocket.addListener(new TransactionBroadcastSequence(transaction, new Asset("1.3.0"), listener)); - mWebSocket.connect(); - - } catch (MalformedAddressException e) { - System.out.println("MalformedAddressException. Msg: " + e.getMessage()); - } catch (MalformedTransactionException e) { - System.out.println("MalformedTransactionException. Msg: " + e.getMessage()); - } catch (NoSuchAlgorithmException e) { - System.out.println("NoSuchAlgorithmException. Msg: " + e.getMessage()); - } catch (IOException e) { - System.out.println("IOException. Msg: " + e.getMessage()); - } catch (WebSocketException e) { - System.out.println("WebSocketException. Msg: " + e.getMessage()); - } - } - - public void testLookupAccounts() { - WitnessResponseListener listener = new WitnessResponseListener() { - @Override - public void onSuccess(WitnessResponse response) { - System.out.println("onSuccess"); - } - - @Override - public void onError(BaseResponse.Error error) { - System.out.println("onError"); - } - }; - - SSLContext context = null; - try { - context = NaiveSSLContext.getInstance("TLS"); - WebSocketFactory factory = new WebSocketFactory(); - - // Set the custom SSL context. - factory.setSSLContext(context); - - WebSocket mWebSocket = factory.createSocket(BLOCK_PAY_DE); - - mWebSocket.addListener(new LookupAccounts("bilthon", listener)); - mWebSocket.connect(); - - } catch (NoSuchAlgorithmException e) { - System.out.println("NoSuchAlgorithmException. Msg: " + e.getMessage()); - } catch (WebSocketException e) { - System.out.println("WebSocketException. Msg: " + e.getMessage()); - } catch (IOException e) { - System.out.println("IOException. Msg: " + e.getMessage()); - } - } - - public void testDecodeMemo() { - - ECKey from = new BrainKey(Main.BILTHON_83_BRAIN_KEY, 0).getPrivateKey(); - PublicKey to = new PublicKey(ECKey.fromPublicOnly(new BrainKey(Main.BILTHON_5_BRAIN_KEY, 0).getPublicKey())); - -// Memo sendMemo = new MemoBuilder().setFromKey(from).setToKey(to).setMessage("test message").build(); - -// JsonElement memoJson = sendMemo.toJsonObject(); -// System.out.println("generated Json : " + memoJson.toString()); -// System.out.println("Decode Memo : " + Memo.decryptMessage(from, to, memoJson.getAsJsonObject().get("message").getAsString(), memoJson.getAsJsonObject().get("nonce").getAsString())); - } - - public void testGetRelativeAccountHistory(){ - WitnessResponseListener listener = new WitnessResponseListener() { - @Override - public void onSuccess(WitnessResponse response) { - System.out.println("onSuccess"); - List transactionHistory = (List) response.result; - System.out.println("Number of transactions: "+transactionHistory.size()); - for(HistoricalTransfer historical : transactionHistory){ - if(historical.getOperation() != null){ - TransferOperation op = historical.getOperation(); - System.out.println("from: "+op.getFrom().getObjectId()+", to: "+op.getTo().getObjectId()+", amount: "+op.getAssetAmount().getAmount()+", block #: "+historical.getBlockNum()); - } - } - } - - @Override - public void onError(BaseResponse.Error error) { - System.out.println("onError"); - } - }; - - SSLContext context = null; - try { - context = NaiveSSLContext.getInstance("TLS"); - WebSocketFactory factory = new WebSocketFactory(); - - // Set the custom SSL context. - factory.setSSLContext(context); - - WebSocket mWebSocket = factory.createSocket(BLOCK_PAY_DE); - - mWebSocket.addListener(new GetRelativeAccountHistory(new UserAccount("1.2.140994"), listener)); - mWebSocket.connect(); - - } catch (NoSuchAlgorithmException e) { - System.out.println("NoSuchAlgorithmException. Msg: " + e.getMessage()); - } catch (WebSocketException e) { - System.out.println("WebSocketException. Msg: " + e.getMessage()); - } catch (IOException e) { - System.out.println("IOException. Msg: " + e.getMessage()); - } - } - - public void testLookupAssetSymbols(){ - WitnessResponseListener listener = new WitnessResponseListener() { - @Override - public void onSuccess(WitnessResponse response) { - System.out.println("onSuccess"); - WitnessResponse> resp = response; - for(Asset asset : resp.result){ - System.out.println("Asset: "+asset.getObjectId()+", Symbol: "+asset.getSymbol()+", supply: "); - } - } - - @Override - public void onError(BaseResponse.Error error) { - System.out.println("onError"); - } - }; - - SSLContext context = null; - try { - context = NaiveSSLContext.getInstance("TLS"); - WebSocketFactory factory = new WebSocketFactory(); - - // Set the custom SSL context. - factory.setSSLContext(context); - - WebSocket mWebSocket = factory.createSocket(BLOCK_PAY_DE); - - ArrayList assets = new ArrayList<>(); - assets.add(new Asset("1.3.0")); - assets.add(new Asset("1.3.120")); - assets.add(new Asset("1.3.121")); - mWebSocket.addListener(new LookupAssetSymbols(assets, listener)); - mWebSocket.connect(); - - } catch (NoSuchAlgorithmException e) { - System.out.println("NoSuchAlgorithmException. Msg: " + e.getMessage()); - } catch (WebSocketException e) { - System.out.println("WebSocketException. Msg: " + e.getMessage()); - } catch (IOException e) { - System.out.println("IOException. Msg: " + e.getMessage()); - } - } - - public void testListAssets(){ - WitnessResponseListener listener = new WitnessResponseListener() { - @Override - public void onSuccess(WitnessResponse response) { - System.out.println("onSuccess"); - List resp = (List) response.result; - System.out.println(String.format("Got %d assets", resp.size())); - int count = 0; - for(Asset asset : resp){ - if(asset.getBitassetId() != null){ - System.out.println("Asset: " + asset.getObjectId() + - ", Symbol: "+asset.getSymbol() + - ", bitasset id: "+asset.getBitassetId()); - count++; - } - } - System.out.println("Got "+count+" smartcoins"); - } - - @Override - public void onError(BaseResponse.Error error) { - System.out.println("onError"); - } - }; - - SSLContext context = null; - try { - context = NaiveSSLContext.getInstance("TLS"); - WebSocketFactory factory = new WebSocketFactory(); - - // Set the custom SSL context. - factory.setSSLContext(context); - - WebSocket mWebSocket = factory.createSocket(BLOCK_PAY_DE); - - mWebSocket.addListener(new ListAssets("", ListAssets.LIST_ALL, listener)); - mWebSocket.connect(); - - } catch (NoSuchAlgorithmException e) { - System.out.println("NoSuchAlgorithmException. Msg: " + e.getMessage()); - } catch (WebSocketException e) { - System.out.println("WebSocketException. Msg: " + e.getMessage()); - } catch (IOException e) { - System.out.println("IOException. Msg: " + e.getMessage()); - } - } - - public void testGetObjects(){ - WitnessResponseListener listener = new WitnessResponseListener() { - @Override - public void onSuccess(WitnessResponse response) { - System.out.println("onSuccess"); - List bitAssetDataArray = (List) response.result; - for(BitAssetData bitAssetData : bitAssetDataArray){ -// System.out.println(String.format("is prediction market: %b", bitAssetData.is_prediction_market)); - System.out.println("base: "+bitAssetData.current_feed.core_exchange_rate.base.getAmount().longValue()); - System.out.println("quote: "+bitAssetData.current_feed.core_exchange_rate.quote.getAmount().longValue()); - } - } - - @Override - public void onError(BaseResponse.Error error) { - System.out.println("onError"); - } - }; - - SSLContext context = null; - try { - context = NaiveSSLContext.getInstance("TLS"); - WebSocketFactory factory = new WebSocketFactory(); - - // Set the custom SSL context. - factory.setSSLContext(context); - - WebSocket mWebSocket = factory.createSocket(BLOCK_PAY_DE); - - ArrayList ids = new ArrayList<>(); - ids.add("2.4.54"); - mWebSocket.addListener(new GetObjects(ids, listener)); - mWebSocket.connect(); - } catch (NoSuchAlgorithmException e) { - System.out.println("NoSuchAlgorithmException. Msg: " + e.getMessage()); - } catch (WebSocketException e) { - System.out.println("WebSocketException. Msg: " + e.getMessage()); - } catch (IOException e) { - System.out.println("IOException. Msg: " + e.getMessage()); - } - } - - public void testGetBlockHeader(){ - WitnessResponseListener listener = new WitnessResponseListener() { - @Override - public void onSuccess(WitnessResponse response) { - System.out.println("onSuccess"); - } - - @Override - public void onError(BaseResponse.Error error) { - System.out.println("onError"); - } - }; - - SSLContext context = null; - try { - context = NaiveSSLContext.getInstance("TLS"); - WebSocketFactory factory = new WebSocketFactory(); - - // Set the custom SSL context. - factory.setSSLContext(context); - - WebSocket mWebSocket = factory.createSocket(BLOCK_PAY_DE); - - - mWebSocket.addListener(new GetBlockHeader(11989481, listener)); - mWebSocket.connect(); - - } catch (NoSuchAlgorithmException e) { - System.out.println("NoSuchAlgorithmException. Msg: " + e.getMessage()); - } catch (WebSocketException e) { - System.out.println("WebSocketException. Msg: " + e.getMessage()); - } catch (IOException e) { - System.out.println("IOException. Msg: " + e.getMessage()); - } - } - - void testGetLimitOrders() { - SSLContext context = null; - try { - context = NaiveSSLContext.getInstance("TLS"); - WebSocketFactory factory = new WebSocketFactory(); - - // Set the custom SSL context. - factory.setSSLContext(context); - - WebSocket mWebSocket = factory.createSocket(BLOCK_PAY_DE); - - Asset base = new Asset("1.3.0", "BTS", 4); - Asset quote = new Asset("1.3.121", "USD", 4); - - mWebSocket.addListener(new GetLimitOrders(base.getObjectId(), quote.getObjectId(), 100, new WitnessResponseListener() { - @Override - public void onSuccess(WitnessResponse response) { - List orders = (List) response.result; - Converter converter = new Converter(); - System.out.println(); - for(LimitOrder order : orders){ -// System.out.println(String.format("base: %d, quote: %d", -// order.sell_price.base.getAmount().longValue(), -// order.sell_price.quote.getAmount().longValue())); - order.getSellPrice().base.getAsset().setPrecision(base.getPrecision()); - order.getSellPrice().quote.getAsset().setPrecision(quote.getPrecision()); - double baseToQuoteExchange = converter.getConversionRate(order.getSellPrice(), Converter.BASE_TO_QUOTE); - double quoteToBaseExchange = converter.getConversionRate(order.getSellPrice(), Converter.QUOTE_TO_BASE); - System.out.println(String.format("base to quote: %.5f, quote to base: %.5f", baseToQuoteExchange, quoteToBaseExchange)); - } - } - - @Override - public void onError(BaseResponse.Error error) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - })); - mWebSocket.connect(); - - } catch (NoSuchAlgorithmException e) { - System.out.println("NoSuchAlgorithmException. Msg: " + e.getMessage()); - } catch (WebSocketException e) { - System.out.println("WebSocketException. Msg: " + e.getMessage()); - } catch (IOException e) { - System.out.println("IOException. Msg: " + e.getMessage()); - } - } - - void testGetTradeHistory() { - SSLContext context = null; - try { - context = NaiveSSLContext.getInstance("TLS"); - WebSocketFactory factory = new WebSocketFactory(); - - // Set the custom SSL context. - factory.setSSLContext(context); - - WebSocket mWebSocket = factory.createSocket(BLOCK_PAY_FR); - - Calendar from = Calendar.getInstance(); - from.roll(Calendar.DAY_OF_MONTH, false); - from.roll(Calendar.DAY_OF_MONTH, false); - Calendar to = Calendar.getInstance(); - to.roll(Calendar.DAY_OF_MONTH, false); - - mWebSocket.addListener(new GetTradeHistory("BTS", "EUR", "20170309T0130000", "20161212T233000",100, new WitnessResponseListener() { - @Override - public void onSuccess(WitnessResponse response) { - List orders = (List) response.result; - for(MarketTrade markeTrade : orders){ - System.out.println("At " + markeTrade.date + " amount " + markeTrade.amount + " value " + markeTrade.value + " price " + markeTrade.price); - } - } - - @Override - public void onError(BaseResponse.Error error) { - } - })); - mWebSocket.connect(); - - } catch (NoSuchAlgorithmException e) { - System.out.println("NoSuchAlgorithmException. Msg: " + e.getMessage()); - } catch (WebSocketException e) { - System.out.println("WebSocketException. Msg: " + e.getMessage()); - } catch (IOException e) { - System.out.println("IOException. Msg: " + e.getMessage()); - } - } - - public void testGetMarketHistory(){ - SSLContext context = null; - - WitnessResponseListener listener = new WitnessResponseListener() { - @Override - public void onSuccess(WitnessResponse response) { - System.out.println("onSuccess"); - List bucketList = (List) response.result; - if(bucketList.size() > 0){ - BucketObject bucket = bucketList.get(0); - Asset base = bucket.key.base; - Asset quote = bucket.key.quote; - base.setPrecision(5); - quote.setPrecision(4); - System.out.println(String.format("Base. symbol: %s, precision: %d", base.getObjectId(), base.getPrecision())); - System.out.println(String.format("Quote. symbol: %s, precision: %d", quote.getObjectId(), quote.getPrecision())); - Converter converter = new Converter(base, quote, bucket); - double rate = converter.getConversionRate(Converter.CLOSE_VALUE, Converter.BASE_TO_QUOTE); - System.out.println(String.format("Conversion rate is 1 base -> %f quote", rate)); - double rate2 = converter.getConversionRate(Converter.CLOSE_VALUE, Converter.QUOTE_TO_BASE); - System.out.println(String.format("Conversion rate is 1 quote -> %f base", rate2)); - } - } - - @Override - public void onError(BaseResponse.Error error) { - System.out.println("onError"); - } - }; - - try { - context = NaiveSSLContext.getInstance("TLS"); - WebSocketFactory factory = new WebSocketFactory(); - - // Set the custom SSL context. - factory.setSSLContext(context); - - WebSocket mWebSocket = factory.createSocket(BLOCK_PAY_FR); - - long posixInstant = 1484089226000l; - Calendar cal = Calendar.getInstance(); - cal.setTimeInMillis(posixInstant); - cal.set(Calendar.SECOND, 0); - cal.set(Calendar.MINUTE, 0); - - Asset USD = new Asset("1.3.121", "USD", 4); - Asset BTS = new Asset("1.3.0", "BTS", 5); - long bucket = 3600; - - mWebSocket.addListener(new GetMarketHistory(BTS, USD, bucket, cal.getTime(), cal.getTime(), listener)); - mWebSocket.connect(); - } catch (NoSuchAlgorithmException e) { - System.out.println("NoSuchAlgorithmException. Msg: " + e.getMessage()); - } catch (WebSocketException e) { - System.out.println("WebSocketException. Msg: " + e.getMessage()); - } catch (IOException e) { - System.out.println("IOException. Msg: " + e.getMessage()); - } - } - - public void testAssetSerialization(){ - ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - DataOutput out = new DataOutputStream(byteArrayOutputStream); - try { - Varint.writeUnsignedVarLong(120, out); - } catch (IOException e) { - e.printStackTrace(); - } - byte[] bytes = byteArrayOutputStream.toByteArray(); - System.out.println("serialized: "+Util.bytesToHex(bytes)); - } - - public void testGetAccountBalances(){ - SSLContext context = null; - - WitnessResponseListener listener = new WitnessResponseListener() { - @Override - public void onSuccess(WitnessResponse response) { - System.out.println("onSuccess"); - } - - @Override - public void onError(BaseResponse.Error error) { - System.out.println("onError"); - } - }; - - try { - context = NaiveSSLContext.getInstance("TLS"); - WebSocketFactory factory = new WebSocketFactory(); - - // Set the custom SSL context. - factory.setSSLContext(context); - - WebSocket mWebSocket = factory.createSocket(BLOCK_PAY_FR); - - UserAccount account = new UserAccount("1.2.138632"); - Asset asset = new Asset("1.3.121"); //USD - ArrayList assetList = new ArrayList<>(); - assetList.add(asset); - mWebSocket.addListener(new GetAccountBalances(account, assetList, listener)); - mWebSocket.connect(); - } catch (NoSuchAlgorithmException e) { - System.out.println("NoSuchAlgorithmException. Msg: " + e.getMessage()); - } catch (WebSocketException e) { - System.out.println("WebSocketException. Msg: " + e.getMessage()); - } catch (IOException e) { - System.out.println("IOException. Msg: " + e.getMessage()); - } - } - - public void testGetAssetHoldersCount(){ - SSLContext context = null; - - WitnessResponseListener listener = new WitnessResponseListener() { - @Override - public void onSuccess(WitnessResponse response) { - System.out.println("onSuccess"); - List holdersCountList = (List) response.result; - for(AssetHolderCount holdersCount : holdersCountList){ - System.out.println(String.format("Asset %s has %d holders", holdersCount.asset.getObjectId(), holdersCount.count)); - } - } - - @Override - public void onError(BaseResponse.Error error) { - System.out.println("onError"); - } - }; - - try { - context = NaiveSSLContext.getInstance("TLS"); - WebSocketFactory factory = new WebSocketFactory(); - - // Set the custom SSL context. - factory.setSSLContext(context); - - WebSocket mWebSocket = factory.createSocket(AMAZON_WITNESS); - - mWebSocket.addListener(new GetAllAssetHolders(listener)); - mWebSocket.connect(); - } catch (NoSuchAlgorithmException e) { - System.out.println("NoSuchAlgorithmException. Msg: " + e.getMessage()); - } catch (WebSocketException e) { - System.out.println("WebSocketException. Msg: " + e.getMessage()); - } catch (IOException e) { - System.out.println("IOException. Msg: " + e.getMessage()); - } - } - - public void testSubscription(WitnessResponseListener listener){ - SSLContext context = null; - - try { - context = NaiveSSLContext.getInstance("TLS"); - WebSocketFactory factory = new WebSocketFactory(); - - // Set the custom SSL context. - factory.setSSLContext(context); - - WebSocket mWebSocket = factory.createSocket(BLOCK_PAY_DE); - - SubscriptionMessagesHub subscriptionHub = new SubscriptionMessagesHub("", ""); - mWebSocket.addListener(subscriptionHub); - mWebSocket.connect(); - subscriptionHub.addSubscriptionListener(new SubscriptionListener() { - @Override - public ObjectType getInterestObjectType() { - return ObjectType.TRANSACTION_OBJECT; - } - - @Override - public void onSubscriptionUpdate(SubscriptionResponse response) { - try{ - List updatedObjects = (List) response.params.get(1); - if(updatedObjects.size() > 0){ - for(Serializable update : updatedObjects){ - if(update instanceof BroadcastedTransaction){ - Transaction t = ((BroadcastedTransaction) update).getTransaction(); - if(t.getOperations().size() > 0){ - for(BaseOperation op : t.getOperations()){ - if(op instanceof TransferOperation){ - System.out.println(String.format("Got transaction from: %s, to: %s", ((TransferOperation) op).getFrom().getObjectId(), ((TransferOperation) op).getTo().getObjectId())); - } - } - } - } - } - } - }catch(Exception e){ - System.out.println("Exception. Msg: "+e.getMessage()); - for(StackTraceElement el : e.getStackTrace()){ - System.out.println(el.getFileName()+"#"+el.getMethodName()+":"+el.getLineNumber()); - } - } - } - }); - } catch (NoSuchAlgorithmException e) { - System.out.println("NoSuchAlgorithmException. Msg: " + e.getMessage()); - } catch (WebSocketException e) { - System.out.println("WebSocketException. Msg: " + e.getMessage()); - } catch (IOException e) { - System.out.println("IOException. Msg: " + e.getMessage()); - } - } -} diff --git a/build.gradle b/build.gradle index e5b03c4..18d4faf 100644 --- a/build.gradle +++ b/build.gradle @@ -3,8 +3,15 @@ allprojects { } subprojects { - apply plugin: "java" repositories { mavenCentral() } } +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:2.3.0' + } +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 685409a..800b496 100644 --- a/gradle.properties +++ b/gradle.properties @@ -30,4 +30,4 @@ POM_LICENCE_NAME=MIT License POM_LICENCE_URL=https://github.com/kenCode-de/graphenej/blob/master/LICENSE POM_LICENCE_DIST=repo POM_DEVELOPER_ID=bilthon -POM_DEVELOPER_NAME=GitHub FullName \ No newline at end of file +POM_DEVELOPER_NAME=bilthon \ No newline at end of file diff --git a/graphenej/build.gradle b/graphenej/build.gradle index d22e299..65fdba3 100644 --- a/graphenej/build.gradle +++ b/graphenej/build.gradle @@ -1,9 +1,10 @@ group 'de.bitsharesmunich' -version '0.1-SNAPSHOT' +version '0.4.0-SNAPSHOT' apply plugin: 'com.android.library' apply from: 'maven-push.gradle' + dependencies { testCompile group: 'junit', name: 'junit', version: '4.12' compile 'com.neovisionaries:nv-websocket-client:1.30' @@ -11,3 +12,24 @@ dependencies { compile group: 'com.google.code.gson', name: 'gson', version: '2.8.0' compile group: "org.tukaani", name: "xz", version: "1.6" } + + +android { + compileSdkVersion 24 + buildToolsVersion "25.0.0" + + defaultConfig { + minSdkVersion 17 + targetSdkVersion 24 + versionCode 1 + versionName "0.4.0" + + vectorDrawables.useSupportLibrary = true + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} \ No newline at end of file diff --git a/graphenej/src/main/AndroidManifest.xml b/graphenej/src/main/AndroidManifest.xml new file mode 100644 index 0000000..ee81480 --- /dev/null +++ b/graphenej/src/main/AndroidManifest.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/AccountUpdateTransactionBuilder.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/AccountUpdateTransactionBuilder.java deleted file mode 100644 index 0e05366..0000000 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/AccountUpdateTransactionBuilder.java +++ /dev/null @@ -1,67 +0,0 @@ -package de.bitsharesmunich.graphenej; - -import de.bitsharesmunich.graphenej.errors.MalformedTransactionException; -import de.bitsharesmunich.graphenej.operations.AccountUpdateOperation; -import org.bitcoinj.core.ECKey; - -import java.util.ArrayList; -import java.util.List; - -/** - * Class used to build a transaction containing an account update operation. - */ -public class AccountUpdateTransactionBuilder extends TransactionBuilder { - private List operations; - private AssetAmount fee; - private UserAccount account; - private Authority owner; - private Authority active; - private AccountOptions new_options; - - public AccountUpdateTransactionBuilder(ECKey privKey) { - super(privKey); - } - - - public AccountUpdateTransactionBuilder setAccont(UserAccount account){ - this.account = account; - return this; - } - - public AccountUpdateTransactionBuilder setOwner(Authority owner){ - this.owner = owner; - return this; - } - - public AccountUpdateTransactionBuilder setActive(Authority active){ - this.active = active; - return this; - } - - public AccountUpdateTransactionBuilder setOptions(AccountOptions options){ - this.new_options = options; - return this; - } - - public AccountUpdateTransactionBuilder setFee(AssetAmount fee){ - this.fee = fee; - return this; - } - - @Override - public Transaction build() throws MalformedTransactionException { - if(account == null){ - throw new MalformedTransactionException("Missing required account information"); - }else{ - operations = new ArrayList<>(); - AccountUpdateOperation operation; - if(fee == null){ - operation = new AccountUpdateOperation(account, owner, active, new_options); - }else{ - operation = new AccountUpdateOperation(account, owner, active, new_options, fee); - } - operations.add(operation); - } - return new Transaction(privateKey, blockData, operations); - } -} diff --git a/settings.gradle b/settings.gradle index a9ac6b8..092cf19 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,3 @@ rootProject.name = "Graphenej" -include ":graphenej", ":app" +include ":graphenej" From be494a81ee42d93c624c57f98be371831fe71235 Mon Sep 17 00:00:00 2001 From: "Nelson R. Perez" Date: Thu, 23 Mar 2017 21:07:59 -0500 Subject: [PATCH 7/7] Adjusting minSdkVersion --- gradle.properties | 4 ++-- graphenej/build.gradle | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/gradle.properties b/gradle.properties index 800b496..b568a9b 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.0 -VERSION_CODE=1 +VERSION_NAME=0.4.1 +VERSION_CODE=3 GROUP=com.github.kenCode-de POM_DESCRIPTION=A Java library for mobile app Developers; Graphene/Bitshares blockchain. diff --git a/graphenej/build.gradle b/graphenej/build.gradle index 65fdba3..e194d62 100644 --- a/graphenej/build.gradle +++ b/graphenej/build.gradle @@ -19,10 +19,10 @@ android { buildToolsVersion "25.0.0" defaultConfig { - minSdkVersion 17 + minSdkVersion 3 targetSdkVersion 24 - versionCode 1 - versionName "0.4.0" + versionCode 3 + versionName "0.4.1" vectorDrawables.useSupportLibrary = true }