diff --git a/src/main/java/com/luminiasoft/bitshares/Main.java b/src/main/java/com/luminiasoft/bitshares/Main.java index 11784a4..40c8ddc 100644 --- a/src/main/java/com/luminiasoft/bitshares/Main.java +++ b/src/main/java/com/luminiasoft/bitshares/Main.java @@ -50,7 +50,7 @@ public class Main { // test.testGetDynamicParams(); // test.testGetRequiredFeesSerialization(); // test.testRequiredFeesResponse(); - test.testTransactionBroadcastSequence(); +// test.testTransactionBroadcastSequence(); // test.testAccountLookupDeserialization(); // test.testPrivateKeyManipulations(); // test.testPublicKeyManipulations(); @@ -69,5 +69,6 @@ public class Main { // test.testCreateBinFile(); // test.testImportBinFile(); //test.testLookupAccounts(); + test.testDecodeMemo(); } } diff --git a/src/main/java/com/luminiasoft/bitshares/Test.java b/src/main/java/com/luminiasoft/bitshares/Test.java index 43e4d99..86a5d20 100644 --- a/src/main/java/com/luminiasoft/bitshares/Test.java +++ b/src/main/java/com/luminiasoft/bitshares/Test.java @@ -1,14 +1,17 @@ package com.luminiasoft.bitshares; +import com.luminiasoft.bitshares.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.JsonElement; import com.google.gson.reflect.TypeToken; import com.luminiasoft.bitshares.errors.MalformedAddressException; import com.luminiasoft.bitshares.errors.MalformedTransactionException; import com.luminiasoft.bitshares.interfaces.WitnessResponseListener; import com.luminiasoft.bitshares.models.*; +import com.luminiasoft.bitshares.objects.MemoBuilder; import com.luminiasoft.bitshares.test.NaiveSSLContext; import com.luminiasoft.bitshares.ws.*; import com.neovisionaries.ws.client.*; @@ -49,8 +52,7 @@ public class Test { 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) { @@ -99,7 +101,7 @@ public class Test { System.out.println("Got empty list!"); } } else if (response.result.getClass() == JsonArray.class) { - System.out.println("Json array : " + ((JsonArray)response.result)); + System.out.println("Json array : " + ((JsonArray) response.result)); } else { System.out.println("Got other: " + response.result.getClass()); } @@ -294,14 +296,14 @@ public class Test { try { // Creating memo - PublicKey from = new PublicKey(ECKey.fromPublicOnly(new BrainKey(Main.BILTHON_83_BRAIN_KEY, 0).getPublicKey())); + PublicKey from = new PublicKey(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 memo = new Memo(from, to, "sample message"); + Memo memo = new MemoBuilder().setFromKey(from).setToKey(to).setMessage("sample message").build(); // Creating transaction Transaction transaction = new TransferTransactionBuilder() - .setSource(new UserAccount("1.2.138632")) // bilthon-83 - .setDestination(new UserAccount("1.2.139313")) // bilthon-5 + .setSource(new UserAccount("1.2.139270")) // bilthon-83 + .setDestination(new UserAccount("1.2.142115")) // bilthon-5 .setAmount(new AssetAmount(UnsignedLong.valueOf(1), new Asset("1.3.0"))) .setFee(new AssetAmount(UnsignedLong.valueOf(264174), new Asset("1.3.0"))) .setPrivateKey(new BrainKey(Main.BILTHON_83_BRAIN_KEY, 0).getPrivateKey()) @@ -357,7 +359,7 @@ public class Test { System.out.println("base58...................: " + Base58.encode(privateKey.getPubKey())); } - public void testPublicKeyManipulations(){ + public void testPublicKeyManipulations() { // PublicKey publicKey = new PublicKey("BTS8RiFgs8HkcVPVobHLKEv6yL3iXcC9SWjbPVS15dDAXLG9GYhnY"); // System.out.println("Public key bytes"); // System.out.println(Util.bytesToHex(publicKey.toBytes())); @@ -427,17 +429,17 @@ public class Test { String suggestion = BrainKey.suggest(words); brainKey = new BrainKey(suggestion, 0); } else { - System.out.println("Using brain key: "+Main.BILTHON_5_BRAIN_KEY); + System.out.println("Using brain key: " + Main.BILTHON_5_BRAIN_KEY); // brainKey = new BrainKey(Main.BILTHON_83_BRAIN_KEY, 0); brainKey = new BrainKey("CYNEBOT LUFBERY DAUNTER TOO SALOOP HOPOFF DIAULOS REV AES TORPOR RECTRIX DEVILRY", 0); } ECKey key = brainKey.getPrivateKey(); - System.out.println("Private key..................: "+Util.bytesToHex(key.getSecretBytes())); + 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()); + System.out.println("Wif from BrainKey............: " + brainKey.getWalletImportFormat()); byte[] uncompressedPubKey = key.decompress().getPubKey(); byte[] compressedPubKey = key.getPubKey(); @@ -604,14 +606,14 @@ public class Test { 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 (MalformedAddressException e) { + System.out.println("MalformedAddressException. Msg: " + e.getMessage()); } catch (MalformedTransactionException e) { - System.out.println("MalformedTransactionException. Msg: "+e.getMessage()); + System.out.println("MalformedTransactionException. Msg: " + e.getMessage()); } } - public void testAccountUpdateOperationBroadcast(){ + public void testAccountUpdateOperationBroadcast() { WitnessResponseListener listener = new WitnessResponseListener() { @Override @@ -653,19 +655,19 @@ public class Test { mWebSocket.connect(); } catch (MalformedAddressException e) { - System.out.println("MalformedAddressException. Msg: "+e.getMessage()); + System.out.println("MalformedAddressException. Msg: " + e.getMessage()); } catch (MalformedTransactionException e) { - System.out.println("MalformedTransactionException. Msg: "+e.getMessage()); + System.out.println("MalformedTransactionException. Msg: " + e.getMessage()); } catch (NoSuchAlgorithmException e) { - System.out.println("NoSuchAlgorithmException. Msg: "+e.getMessage()); + System.out.println("NoSuchAlgorithmException. Msg: " + e.getMessage()); } catch (IOException e) { - System.out.println("IOException. Msg: "+e.getMessage()); + System.out.println("IOException. Msg: " + e.getMessage()); } catch (WebSocketException e) { - System.out.println("WebSocketException. Msg: "+e.getMessage()); + System.out.println("WebSocketException. Msg: " + e.getMessage()); } } - public void testLookupAccounts(){ + public void testLookupAccounts() { WitnessResponseListener listener = new WitnessResponseListener() { @Override public void onSuccess(WitnessResponse response) { @@ -692,12 +694,24 @@ public class Test { mWebSocket.connect(); } catch (NoSuchAlgorithmException e) { - System.out.println("NoSuchAlgorithmException. Msg: "+e.getMessage()); + System.out.println("NoSuchAlgorithmException. Msg: " + e.getMessage()); } catch (WebSocketException e) { - System.out.println("WebSocketException. Msg: "+e.getMessage()); + System.out.println("WebSocketException. Msg: " + e.getMessage()); } catch (IOException e) { - System.out.println("IOException. Msg: "+e.getMessage()); + System.out.println("IOException. Msg: " + e.getMessage()); } } - + + public void testDecodeMemo() { + + PublicKey from = new PublicKey((new BrainKey(Main.BILTHON_83_BRAIN_KEY, 0).getPrivateKey())); + PublicKey to = new PublicKey(new BrainKey(Main.BILTHON_5_BRAIN_KEY, 0).getPrivateKey()); + + 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.decodeMessage(from, to, memoJson.getAsJsonObject().get("message").getAsString(), memoJson.getAsJsonObject().get("nonce").getAsString())); + + } } diff --git a/src/main/java/com/luminiasoft/bitshares/TransferOperation.java b/src/main/java/com/luminiasoft/bitshares/TransferOperation.java index 47e3586..042330d 100644 --- a/src/main/java/com/luminiasoft/bitshares/TransferOperation.java +++ b/src/main/java/com/luminiasoft/bitshares/TransferOperation.java @@ -1,5 +1,6 @@ package com.luminiasoft.bitshares; +import com.luminiasoft.bitshares.objects.Memo; import com.google.common.primitives.Bytes; import com.google.gson.*; @@ -65,6 +66,7 @@ public class TransferOperation extends BaseOperation { @Override public byte[] toBytes() { + System.out.println("ne toBytes"); byte[] feeBytes = fee.toBytes(); byte[] fromBytes = from.toBytes(); byte[] toBytes = to.toBytes(); diff --git a/src/main/java/com/luminiasoft/bitshares/TransferTransactionBuilder.java b/src/main/java/com/luminiasoft/bitshares/TransferTransactionBuilder.java index d191040..ca6e5eb 100644 --- a/src/main/java/com/luminiasoft/bitshares/TransferTransactionBuilder.java +++ b/src/main/java/com/luminiasoft/bitshares/TransferTransactionBuilder.java @@ -1,5 +1,6 @@ package com.luminiasoft.bitshares; +import com.luminiasoft.bitshares.objects.Memo; import com.luminiasoft.bitshares.errors.MalformedTransactionException; import org.bitcoinj.core.ECKey; diff --git a/src/main/java/com/luminiasoft/bitshares/Memo.java b/src/main/java/com/luminiasoft/bitshares/objects/Memo.java similarity index 50% rename from src/main/java/com/luminiasoft/bitshares/Memo.java rename to src/main/java/com/luminiasoft/bitshares/objects/Memo.java index 2a7428e..2da8f76 100644 --- a/src/main/java/com/luminiasoft/bitshares/Memo.java +++ b/src/main/java/com/luminiasoft/bitshares/objects/Memo.java @@ -1,8 +1,10 @@ -package com.luminiasoft.bitshares; +package com.luminiasoft.bitshares.objects; import com.google.common.primitives.Bytes; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.luminiasoft.bitshares.PublicKey; +import com.luminiasoft.bitshares.Util; import com.luminiasoft.bitshares.crypto.SecureRandomStrengthener; import com.luminiasoft.bitshares.interfaces.ByteSerializable; import com.luminiasoft.bitshares.interfaces.JsonSerializable; @@ -10,8 +12,6 @@ import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; -import org.bitcoinj.core.Base58; -import org.bitcoinj.core.ECKey; /** * Created by nelson on 11/9/16. @@ -25,47 +25,25 @@ public class Memo implements ByteSerializable, JsonSerializable { private PublicKey from; private PublicKey to; - private byte[] nonce = new byte[8]; + private final byte[] nonce = new byte[8]; private byte[] message; + /** + * Empty Constructor + */ public Memo() { this.from = null; this.to = null; - this.nonce = null; this.message = null; } - - public Memo(PublicKey from, PublicKey to, String message) { - this.from = from; - this.to = to; - this.message = message.getBytes(); - this.encodeMessage(from.getKey(), to.getKey(), this.message, 0); - } - + /** - * Constructor for receiving Message - * - * @param from Public Key of the source - * @param to Our Public Key - * @param message The message cnoded - * @param nonce The nonce used for encoding the message + * Implement metod, serialized this Object + * @return the byte array of this object serialized */ - public Memo(PublicKey from, PublicKey to, String message, String nonce) { - this.from = from; - this.to = to; - this.message = new BigInteger(message, 16).toByteArray(); - byte[] firstNonce = new BigInteger(nonce,10).toByteArray(); - this.nonce = new byte[8]; - System.arraycopy(firstNonce, firstNonce.length-8, this.nonce, 0, this.nonce.length); - - //this.nonce = new BigInteger(nonce,10).toByteArray(); - - - } - @Override public byte[] toBytes() { - if ((this.from == null) || (this.to == null) || (this.nonce == null) || (this.message == null)) { + if ((this.from == null) || (this.to == null) || (this.message == null)) { return new byte[]{(byte) 0}; } else { byte[] nonceformat = new byte[nonce.length]; @@ -76,40 +54,57 @@ public class Memo implements ByteSerializable, JsonSerializable { } } - public void encodeMessage(ECKey fromKey, ECKey toKey, byte[] msg) { - this.encodeMessage(fromKey, toKey, msg, 0); + /** + * Encode a memo message using the input source key and destination key + * @param fromKey The source destination, need to have a private key accesss + * @param toKey The destination, needs only the public key access + * @param msg The message in bytes + * @return a Memo corresponding with the message encoding + */ + public static Memo encodeMessage(PublicKey fromKey, PublicKey toKey, byte[] msg) { + return encodeMessage(fromKey, toKey, msg, 0); } - public void encodeMessage(ECKey fromKey, ECKey toKey, byte[] msg, long custom_nonce) { + /** + * Encode a message a return a memo with that message encoded + * + * @param fromKey The source destination, need to have a private key accesss + * @param toKey The destination, needs only the public key access + * @param msg The message in bytes + * @param custom_nonce the custom nonce to be use or 0 to create a new one + * @return a Memo corresponding with the message encoding + */ + public static Memo encodeMessage(PublicKey fromKey, PublicKey toKey, byte[] msg, long custom_nonce) { + Memo memo = new Memo(); try { MessageDigest md = MessageDigest.getInstance("SHA-256"); - this.from = new PublicKey(fromKey); - this.to = new PublicKey(toKey); + memo.from = fromKey; + memo.to = toKey; if (custom_nonce == 0) { SecureRandomStrengthener randomStrengthener = SecureRandomStrengthener.getInstance(); //randomStrengthener.addEntropySource(new AndroidRandomSource()); SecureRandom secureRandom = randomStrengthener.generateAndSeedRandomNumberGenerator(); - secureRandom.nextBytes(nonce); + secureRandom.nextBytes(memo.nonce); long time = System.currentTimeMillis(); for (int i = 7; i >= 1; i--) { - this.nonce[i] = (byte) (time & 0xff); + memo.nonce[i] = (byte) (time & 0xff); time = time / 0x100; } } else { for (int i = 7; i >= 0; i--) { - this.nonce[i] = (byte) (custom_nonce & 0xff); + memo.nonce[i] = (byte) (custom_nonce & 0xff); custom_nonce = custom_nonce / 0x100; } } - byte[] secret = toKey.getPubKeyPoint().multiply(fromKey.getPrivKey()).normalize().getXCoord().getEncoded(); - byte[] finalKey = new byte[secret.length + this.nonce.length]; + byte[] secret = toKey.getKey().getPubKeyPoint().multiply(fromKey.getKey().getPrivKey()).normalize().getXCoord().getEncoded(); + byte[] finalKey = new byte[secret.length + memo.nonce.length]; System.arraycopy(secret, 0, finalKey, 0, secret.length); - System.arraycopy(this.nonce, 0, finalKey, secret.length, this.nonce.length); + System.arraycopy(memo.nonce, 0, finalKey, secret.length, memo.nonce.length); byte[] sha256Msg = md.digest(msg); byte[] serialChecksum = new byte[4]; @@ -117,27 +112,57 @@ public class Memo implements ByteSerializable, JsonSerializable { byte[] msgFinal = new byte[serialChecksum.length + msg.length]; System.arraycopy(serialChecksum, 0, msgFinal, 0, serialChecksum.length); System.arraycopy(msg, 0, msgFinal, serialChecksum.length, msg.length); - - this.message = Util.encryptAES(msgFinal, finalKey); + memo.message = Util.encryptAES(msgFinal, finalKey); } catch (NoSuchAlgorithmException ex) { - } + return memo; } - public String decodeMessage() { + /** + * returns the string coreesponding a encode memo + * + * @param fromKey The soruce key, need to have public key only + * @param toKey The destination key, need to have private key access + * @param msg The message to be decoded + * @param nonce The nonce used in the decoded message + * @return The message + */ + public static String decodeMessage(PublicKey fromKey, PublicKey toKey, byte[] msg, byte[] nonce) { - byte[] secret = this.from.getKey().getPubKeyPoint().multiply(this.to.getKey().getPrivKey()).normalize().getXCoord().getEncoded(); - byte[] finalKey = new byte[secret.length + this.nonce.length]; + byte[] secret = fromKey.getKey().getPubKeyPoint().multiply(toKey.getKey().getPrivKey()).normalize().getXCoord().getEncoded(); + byte[] finalKey = new byte[secret.length + nonce.length]; System.arraycopy(secret, 0, finalKey, 0, secret.length); - System.arraycopy(this.nonce, 0, finalKey, secret.length, this.nonce.length); + System.arraycopy(nonce, 0, finalKey, secret.length, nonce.length); - byte[] msgFinal = Util.decryptAES(this.message, finalKey); + byte[] msgFinal = Util.decryptAES(msg, finalKey); byte[] decodedMsg = new byte[msgFinal.length - 4]; //TODO verify checksum for integrity System.arraycopy(msgFinal, 4, decodedMsg, 0, decodedMsg.length); return new String(decodedMsg); } + /** + * returns the string coreesponding a encode memo + * + * @param fromKey The soruce key, need to have public key only + * @param toKey The destination key, need to have private key access + * @param message The message to be decoded + * @param nonce The nonce used in the decoded message + * @return The message + */ + public static String decodeMessage(PublicKey fromKey, PublicKey toKey, String message, String nonce) { + byte[] msg = new BigInteger(message, 16).toByteArray(); + if (msg[0] == 0) { + byte[] temp = new byte[msg.length - 1]; + System.arraycopy(msg, 1, temp, 0, temp.length); + msg = temp; + } + byte[] firstNonce = new BigInteger(nonce, 10).toByteArray(); + byte[] nonceByte = new byte[8]; + System.arraycopy(firstNonce, firstNonce.length - 8, nonceByte, 0, nonceByte.length); + return decodeMessage(fromKey, toKey, msg, nonceByte); + } + @Override public String toJsonString() { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. diff --git a/src/main/java/com/luminiasoft/bitshares/objects/MemoBuilder.java b/src/main/java/com/luminiasoft/bitshares/objects/MemoBuilder.java new file mode 100644 index 0000000..c329758 --- /dev/null +++ b/src/main/java/com/luminiasoft/bitshares/objects/MemoBuilder.java @@ -0,0 +1,74 @@ +package com.luminiasoft.bitshares.objects; + +import com.luminiasoft.bitshares.PublicKey; + +/** + * Class to build a Memo Object + * @author henry 10/12/2016 + */ +public class MemoBuilder { + + private PublicKey fromKey; + private PublicKey toKey; + private String message; + private long nonce = 0; + + /** + * Empty Constructor + */ + public MemoBuilder() { + } + + /** + * Set the key of the Source, needs to have a private Key access + * @param fromKey The Public Key of the sender + * @return The MemoBuilder + */ + public MemoBuilder setFromKey(PublicKey fromKey) { + this.fromKey = fromKey; + return this; + } + + /** + * Set the key of the destination, only need the public key. + * @param toKey The Public Key of the receiver + * @return The MemoBuilder + */ + public MemoBuilder setToKey(PublicKey toKey) { + this.toKey = toKey; + return this; + } + + /** + * Set the message to be send + * @param message The message as a String + * @return The MemoBuilder + */ + public MemoBuilder setMessage(String message) { + this.message = message; + return this; + } + + /** + * (Optional) Sets a custom nonce + * @param nonce The custom nonce + * @return The MemoBuilder + */ + public MemoBuilder setNone(Long nonce) { + this.nonce = nonce; + return this; + } + + /** + * Biulds the memo object + * @return The Memo object + */ + public Memo build() { + //Todo unencode key + if (nonce == 0) { + return Memo.encodeMessage(fromKey, toKey, message.getBytes()); + } + return Memo.encodeMessage(fromKey, toKey, message.getBytes(), nonce); + } + +}