limit_order_create_operation-bearing tx is successfully being created and accepted by the network

This commit is contained in:
Nelson R. Perez 2017-03-20 19:21:04 -05:00
parent 5ee4ef77f2
commit a9a550491b
6 changed files with 212 additions and 20 deletions

View file

@ -50,7 +50,7 @@ public class Main {
// e.printStackTrace(); // e.printStackTrace();
// } // }
// test.testCustomSerializer(); // test.testCustomSerializer();
test.testUserAccountSerialization(); // test.testUserAccountSerialization();
// test.testTransactionSerialization(); // test.testTransactionSerialization();
// test.testLoginSerialization(); // test.testLoginSerialization();
// test.testNetworkBroadcastSerialization(); // test.testNetworkBroadcastSerialization();
@ -89,7 +89,7 @@ public class Main {
// test.testGetObjects(); // test.testGetObjects();
// test.testGetBlockHeader(); // test.testGetBlockHeader();
// test.testGetLimitOrders(); // test.testGetLimitOrders();
// test.testGetTradeHistory(); test.testGetTradeHistory();
// test.testAssetSerialization(); // test.testAssetSerialization();
// test.testGetMarketHistory(); // test.testGetMarketHistory();
// test.testGetAccountBalances(); // test.testGetAccountBalances();

View file

@ -276,12 +276,14 @@ public class Test {
@Override @Override
public void onSuccess(WitnessResponse response) { public void onSuccess(WitnessResponse response) {
System.out.println("onSuccess"); System.out.println("onSuccess");
System.out.println("Callback.Thread. name: "+Thread.currentThread().getName()+", id: "+Thread.currentThread().getId());
} }
@Override @Override
public void onError(BaseResponse.Error error) { public void onError(BaseResponse.Error error) {
System.out.println("onError"); System.out.println("onError");
System.out.println(error.data.message); 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.addListener(new TransactionBroadcastSequence(transaction, new Asset("1.3.0"), listener));
mWebSocket.connect(); mWebSocket.connect();
System.out.println("Main.Thread. name: "+Thread.currentThread().getName()+", id: "+Thread.currentThread().getId());
} catch (MalformedOperationException e) { } catch (MalformedOperationException e) {
System.out.println("MalformedTransactionException. Msg: " + e.getMessage()); System.out.println("MalformedTransactionException. Msg: " + e.getMessage());
} catch (IOException e) { } catch (IOException e) {
@ -1189,7 +1191,7 @@ public class Test {
Calendar to = Calendar.getInstance(); Calendar to = Calendar.getInstance();
to.roll(Calendar.DAY_OF_MONTH, false); 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 @Override
public void onSuccess(WitnessResponse response) { public void onSuccess(WitnessResponse response) {
List<MarketTrade> orders = (List<MarketTrade>) response.result; List<MarketTrade> orders = (List<MarketTrade>) response.result;

View file

@ -133,7 +133,7 @@ public class TransactionBroadcastSequence extends WebSocketAdapter {
websocket.sendText(call.toJsonString()); websocket.sendText(call.toJsonString());
}else if(baseResponse.id >= BROADCAST_TRANSACTION){ }else if(baseResponse.id >= BROADCAST_TRANSACTION){
Type WitnessResponseType = new TypeToken<WitnessResponse<String>>(){}.getType(); Type WitnessResponseType = new TypeToken<WitnessResponse<String>>(){}.getType();
WitnessResponse<WitnessResponse<String>> witnessResponse = gson.fromJson(response, WitnessResponseType); WitnessResponse<String> witnessResponse = gson.fromJson(response, WitnessResponseType);
mListener.onSuccess(witnessResponse); mListener.onSuccess(witnessResponse);
websocket.disconnect(); websocket.disconnect();
} }

View file

@ -9,9 +9,19 @@ import de.bitsharesmunich.graphenej.*;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.TimeZone;
/** /**
* Operation used to denote the creation of a limit order on the blockchain. * 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 { public class LimitOrderCreateOperation extends BaseOperation {
// Number of bytes used for the expiration field. // Number of bytes used for the expiration field.
@ -27,15 +37,22 @@ public class LimitOrderCreateOperation extends BaseOperation {
// Inner fields of a limit order // Inner fields of a limit order
private AssetAmount fee; private AssetAmount fee;
private UserAccount seller; private UserAccount seller;
private AssetAmount toSell; private AssetAmount amountToSell;
private AssetAmount minToReceive; private AssetAmount minToReceive;
private int expiration; private int expiration;
private boolean fillOrKill; 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){ public LimitOrderCreateOperation(UserAccount seller, AssetAmount toSell, AssetAmount minToReceive, int expiration, boolean fillOrKill){
super(OperationType.LIMIT_ORDER_CREATE_OPERATION); super(OperationType.LIMIT_ORDER_CREATE_OPERATION);
this.seller = seller; this.seller = seller;
this.toSell = toSell; this.amountToSell = toSell;
this.minToReceive = minToReceive; this.minToReceive = minToReceive;
this.expiration = expiration; this.expiration = expiration;
this.fillOrKill = fillOrKill; this.fillOrKill = fillOrKill;
@ -54,11 +71,14 @@ public class LimitOrderCreateOperation extends BaseOperation {
if(fee != null) if(fee != null)
jsonObject.add(KEY_FEE, fee.toJsonObject()); jsonObject.add(KEY_FEE, fee.toJsonObject());
jsonObject.addProperty(KEY_SELLER, seller.toJsonString()); 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()); jsonObject.add(KEY_MIN_TO_RECEIVE, minToReceive.toJsonObject());
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(Util.TIME_DATE_FORMAT); 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()); jsonObject.add(KEY_EXTENSIONS, new JsonArray());
array.add(jsonObject); array.add(jsonObject);
return array; return array;
@ -73,7 +93,7 @@ public class LimitOrderCreateOperation extends BaseOperation {
public byte[] toBytes() { public byte[] toBytes() {
byte[] feeBytes = this.fee.toBytes(); byte[] feeBytes = this.fee.toBytes();
byte[] sellerBytes = this.seller.toBytes(); byte[] sellerBytes = this.seller.toBytes();
byte[] amountBytes = this.toSell.toBytes(); byte[] amountBytes = this.amountToSell.toBytes();
byte[] minAmountBytes = this.minToReceive.toBytes(); byte[] minAmountBytes = this.minToReceive.toBytes();
ByteBuffer buffer = ByteBuffer.allocate(EXPIRATION_BYTE_LENGTH); ByteBuffer buffer = ByteBuffer.allocate(EXPIRATION_BYTE_LENGTH);
@ -81,6 +101,8 @@ public class LimitOrderCreateOperation extends BaseOperation {
byte[] expirationBytes = Util.revertBytes(buffer.array()); byte[] expirationBytes = Util.revertBytes(buffer.array());
byte[] fillOrKill = this.fillOrKill ? new byte[]{ 0x1 } : new byte[]{ 0x0 }; 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);
} }
} }

View file

@ -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<String> 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<BaseOperation> 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<BaseOperation> 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<BaseOperation> operationList = new ArrayList<>();
operationList.add(operation);
// Broadcasting transaction
broadcastTransaction(privateKey, operationList, listener);
}
}

View file

@ -17,11 +17,11 @@ import static org.junit.Assert.*;
* Created by nelson on 3/6/17. * Created by nelson on 3/6/17.
*/ */
public class LimitOrderCreateOperationTest { public class LimitOrderCreateOperationTest {
private final int AMOUNT_TO_SELL = 25000000; private static final int AMOUNT_TO_SELL = 25000000;
private final int MIN_TO_RECEIVE = 1; private static final int MIN_TO_RECEIVE = 1;
private final Asset CORE_ASSET = new Asset("1.3.0"); private static final Asset CORE_ASSET = new Asset("1.3.0");
private final Asset BIT_USD = new Asset("1.3.121"); private static final Asset BIT_USD = new Asset("1.3.121");
private final int DEFAULT_EXPIRATION = 1488831620; // 2017-03-06T20:20:20 private static final int DEFAULT_EXPIRATION = 1488831620; // 2017-03-06T20:20:20
private UserAccount seller; private UserAccount seller;
private AssetAmount amountToSell; private AssetAmount amountToSell;
@ -43,14 +43,14 @@ public class LimitOrderCreateOperationTest {
LimitOrderCreateOperation operation = new LimitOrderCreateOperation(seller, amountToSell, minToReceive, expiration, true); LimitOrderCreateOperation operation = new LimitOrderCreateOperation(seller, amountToSell, minToReceive, expiration, true);
operation.setFee(new AssetAmount(UnsignedLong.valueOf(2), CORE_ASSET)); operation.setFee(new AssetAmount(UnsignedLong.valueOf(2), CORE_ASSET));
byte[] serialized = operation.toBytes(); byte[] serialized = operation.toBytes();
Assert.assertArrayEquals("Correct serialization", serialized, Util.hexToBytes("020000000000000000cbe10840787d01000000000001000000000000007984c4bd5801")); Assert.assertArrayEquals("Correct serialization", serialized, Util.hexToBytes("020000000000000000cbe10840787d01000000000001000000000000007984c4bd580100"));
Assert.assertThat("Incorrect serialization", serialized, IsNot.not(IsEqual.equalTo("020000000000000000cbe10840787d01000000000001000000000000007984c4bd5800"))); Assert.assertThat("Incorrect serialization", serialized, IsNot.not(IsEqual.equalTo("020000000000000000cbe10840787d01000000000001000000000000007984c4bd580000")));
// Testing serialization of operation with fillOrKill parameter == false // Testing serialization of operation with fillOrKill parameter == false
operation = new LimitOrderCreateOperation(seller, amountToSell, minToReceive, expiration, false); operation = new LimitOrderCreateOperation(seller, amountToSell, minToReceive, expiration, false);
operation.setFee(new AssetAmount(UnsignedLong.valueOf(2), CORE_ASSET)); operation.setFee(new AssetAmount(UnsignedLong.valueOf(2), CORE_ASSET));
serialized = operation.toBytes(); serialized = operation.toBytes();
Assert.assertArrayEquals("Correct serialization", serialized, Util.hexToBytes("020000000000000000cbe10840787d01000000000001000000000000007984c4bd5800")); Assert.assertArrayEquals("Correct serialization", serialized, Util.hexToBytes("020000000000000000cbe10840787d01000000000001000000000000007984c4bd580000"));
Assert.assertThat("Incorrect serialization", serialized, IsNot.not(IsEqual.equalTo("020000000000000000cbe10840787d01000000000001000000000000007984c4bd5801"))); Assert.assertThat("Incorrect serialization", serialized, IsNot.not(IsEqual.equalTo("020000000000000000cbe10840787d01000000000001000000000000007984c4bd580100")));
} }
} }