From a9a550491b4a0b4b3cd76b6c8a21f1d4fa8e7b13 Mon Sep 17 00:00:00 2001 From: "Nelson R. Perez" Date: Mon, 20 Mar 2017 19:21:04 -0500 Subject: [PATCH] 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