diff --git a/src/main/java/com/luminiasoft/bitshares/Address.java b/src/main/java/com/luminiasoft/bitshares/Address.java index f2e8703..d363500 100644 --- a/src/main/java/com/luminiasoft/bitshares/Address.java +++ b/src/main/java/com/luminiasoft/bitshares/Address.java @@ -1,57 +1,66 @@ package com.luminiasoft.bitshares; import com.google.common.primitives.Bytes; -import com.google.gson.internal.LinkedTreeMap; -import static com.luminiasoft.bitshares.Test.OPENLEDGER_WITNESS_URL; -import com.luminiasoft.bitshares.interfaces.WitnessResponseListener; -import com.luminiasoft.bitshares.models.BaseResponse; -import com.luminiasoft.bitshares.models.WitnessResponse; -import com.luminiasoft.bitshares.test.NaiveSSLContext; -import com.luminiasoft.bitshares.ws.GetAccountNameById; -import com.luminiasoft.bitshares.ws.GetAccountsByAddress; -import com.neovisionaries.ws.client.WebSocket; -import com.neovisionaries.ws.client.WebSocketException; -import com.neovisionaries.ws.client.WebSocketFactory; -import java.io.IOException; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; +import com.luminiasoft.bitshares.errors.MalformedAddressException; import org.bitcoinj.core.Base58; import org.bitcoinj.core.ECKey; import org.spongycastle.crypto.digests.RIPEMD160Digest; +import org.spongycastle.math.ec.ECPoint; import java.util.Arrays; -import java.util.List; -import javafx.util.Pair; -import javax.net.ssl.SSLContext; +import java.util.IllegalFormatException; /** * Class used to encapsulate address-related operations. */ public class Address { - public final static String DEFAULT_PREFIX = "BTS"; + public final static String BITSHARES_PREFIX = "BTS"; - private ECKey key; + private PublicKey publicKey; private String prefix; public Address(ECKey key) { - this.key = key; - this.prefix = DEFAULT_PREFIX; + this.publicKey = new PublicKey(key); + this.prefix = BITSHARES_PREFIX; } public Address(ECKey key, String prefix) { - this.key = key; + this.publicKey = new PublicKey(key); this.prefix = prefix; } + public Address(String address) throws MalformedAddressException { + this.prefix = address.substring(0, 3); + byte[] decoded = Base58.decode(address.substring(3, address.length())); + byte[] pubKey = Arrays.copyOfRange(decoded, 0, decoded.length - 4); + byte[] checksum = Arrays.copyOfRange(decoded, decoded.length - 4, decoded.length); + publicKey = new PublicKey(ECKey.fromPublicOnly(pubKey)); + byte[] calculatedChecksum = calculateChecksum(pubKey); + for(int i = 0; i < calculatedChecksum.length; i++){ + if(checksum[i] != calculatedChecksum[i]){ + throw new MalformedAddressException("Checksum error"); + } + } + } + + public PublicKey getPublicKey(){ + return this.publicKey; + } + @Override public String toString() { - byte[] pubKey = key.getPubKey(); - byte[] checksum = new byte[160 / 8]; - RIPEMD160Digest ripemd160Digest = new RIPEMD160Digest(); - ripemd160Digest.update(pubKey, 0, pubKey.length); - ripemd160Digest.doFinal(checksum, 0); - byte[] pubKeyChecksummed = Bytes.concat(pubKey, Arrays.copyOfRange(checksum, 0, 4)); + byte[] pubKey = this.publicKey.toBytes(); + byte[] checksum = calculateChecksum(pubKey); + byte[] pubKeyChecksummed = Bytes.concat(pubKey, checksum); return this.prefix + Base58.encode(pubKeyChecksummed); } + + private byte[] calculateChecksum(byte[] data){ + byte[] checksum = new byte[160 / 8]; + RIPEMD160Digest ripemd160Digest = new RIPEMD160Digest(); + ripemd160Digest.update(data, 0, data.length); + ripemd160Digest.doFinal(checksum, 0); + return Arrays.copyOfRange(checksum, 0, 4); + } } diff --git a/src/main/java/com/luminiasoft/bitshares/Authority.java b/src/main/java/com/luminiasoft/bitshares/Authority.java new file mode 100644 index 0000000..eae9224 --- /dev/null +++ b/src/main/java/com/luminiasoft/bitshares/Authority.java @@ -0,0 +1,35 @@ +package com.luminiasoft.bitshares; + +import com.google.gson.JsonElement; +import com.luminiasoft.bitshares.errors.MalformedAddressException; +import com.luminiasoft.bitshares.interfaces.JsonSerializable; + +import java.util.HashMap; + +/** + * Created by nelson on 11/30/16. + */ +public class Authority implements JsonSerializable { + private long weight_threshold; + private HashMap address_auths; + private HashMap account_auths; + private HashMap key_auths; + + public Authority(HashMap keyAuths) throws MalformedAddressException { + key_auths = new HashMap(); + for(String key : keyAuths.keySet()){ + Address address = new Address(key); + key_auths.put(address.getPublicKey(), keyAuths.get(key)); + } + } + + @Override + public String toJsonString() { + return null; + } + + @Override + public JsonElement toJsonObject() { + return null; + } +} diff --git a/src/main/java/com/luminiasoft/bitshares/Main.java b/src/main/java/com/luminiasoft/bitshares/Main.java index 01839ae..69da478 100644 --- a/src/main/java/com/luminiasoft/bitshares/Main.java +++ b/src/main/java/com/luminiasoft/bitshares/Main.java @@ -38,7 +38,7 @@ public class Main { // e.printStackTrace(); // } // test.testCustomSerializer(); - test.testUserAccountSerialization(); +// test.testUserAccountSerialization(); // test.testTransactionSerialization(); // test.testLoginSerialization(); // test.testNetworkBroadcastSerialization(); @@ -49,6 +49,7 @@ public class Main { // test.testTransactionBroadcastSequence(); // test.testAccountLookupDeserialization(); // test.testPrivateKeyManipulations(); + test.testPublicKeyManipulations(); // test.testGetAccountByName(); // test.testGetRequiredFees(); // test.testRandomNumberGeneration(); diff --git a/src/main/java/com/luminiasoft/bitshares/PublicKey.java b/src/main/java/com/luminiasoft/bitshares/PublicKey.java new file mode 100644 index 0000000..31961ae --- /dev/null +++ b/src/main/java/com/luminiasoft/bitshares/PublicKey.java @@ -0,0 +1,20 @@ +package com.luminiasoft.bitshares; + +import com.luminiasoft.bitshares.interfaces.ByteSerializable; +import org.bitcoinj.core.ECKey; + +/** + * Created by nelson on 11/30/16. + */ +public class PublicKey implements ByteSerializable { + private ECKey publicKey; + + public PublicKey(ECKey key) { + this.publicKey = key; + } + + @Override + public byte[] toBytes() { + return publicKey.getPubKey(); + } +} diff --git a/src/main/java/com/luminiasoft/bitshares/Test.java b/src/main/java/com/luminiasoft/bitshares/Test.java index 02a25de..01bf02f 100644 --- a/src/main/java/com/luminiasoft/bitshares/Test.java +++ b/src/main/java/com/luminiasoft/bitshares/Test.java @@ -4,6 +4,7 @@ import com.google.common.primitives.UnsignedLong; import com.google.gson.Gson; import com.google.gson.GsonBuilder; 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.*; @@ -12,20 +13,14 @@ import com.luminiasoft.bitshares.ws.*; import com.neovisionaries.ws.client.*; import org.bitcoinj.core.*; import org.spongycastle.crypto.Digest; -import org.spongycastle.crypto.digests.RIPEMD128Digest; import org.spongycastle.crypto.digests.RIPEMD160Digest; import org.spongycastle.crypto.digests.SHA512Digest; import org.spongycastle.crypto.prng.DigestRandomGenerator; import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLServerSocketFactory; -import javax.net.ssl.SSLSocketFactory; import java.io.*; import java.lang.reflect.Type; -import java.net.URL; -import java.nio.file.Paths; import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; import java.text.SimpleDateFormat; import java.util.*; import java.util.logging.Level; @@ -40,10 +35,10 @@ public class Test { public static final String OPENLEDGER_WITNESS_URL = "wss://bitshares.openledger.info/ws"; // public static final String WITNESS_URL = "wss://fr.blockpay.ch:8089"; - private Transaction transaction; + private TransferOperation transferOperation; - public Transaction getTransaction() { - return transaction; + public TransferOperation getTransferOperation() { + return transferOperation; } private WitnessResponseListener mListener = new WitnessResponseListener() { @@ -114,27 +109,27 @@ public class Test { }; public ECKey.ECDSASignature testSigning() { - byte[] serializedTransaction = this.transaction.toBytes(); + byte[] serializedTransaction = this.transferOperation.toBytes(); Sha256Hash hash = Sha256Hash.wrap(Sha256Hash.hash(serializedTransaction)); byte[] bytesDigest = hash.getBytes(); - ECKey sk = transaction.getPrivateKey(); + ECKey sk = transferOperation.getPrivateKey(); ECKey.ECDSASignature signature = sk.sign(hash); return signature; } public String testSigningMessage() { - byte[] serializedTransaction = this.transaction.toBytes(); + byte[] serializedTransaction = this.transferOperation.toBytes(); Sha256Hash hash = Sha256Hash.wrap(Sha256Hash.hash(serializedTransaction)); - ECKey sk = transaction.getPrivateKey(); + ECKey sk = transferOperation.getPrivateKey(); return sk.signMessage(hash.toString()); } public byte[] signMessage() { - byte[] serializedTransaction = this.transaction.toBytes(); + byte[] serializedTransaction = this.transferOperation.toBytes(); Sha256Hash hash = Sha256Hash.wrap(Sha256Hash.hash(serializedTransaction)); System.out.println(">> digest <<"); System.out.println(Util.bytesToHex(hash.getBytes())); - ECKey sk = transaction.getPrivateKey(); + ECKey sk = transferOperation.getPrivateKey(); System.out.println("Private key bytes"); System.out.println(Util.bytesToHex(sk.getPrivKeyBytes())); boolean isCanonical = false; @@ -189,9 +184,9 @@ public class Test { 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 Transfer(from, to, amount, fee)); - this.transaction = new Transaction(Main.WIF, blockData, operations); - byte[] serializedTransaction = this.transaction.toBytes(); - System.out.println("Serialized transaction"); + this.transferOperation = new TransferOperation(Main.WIF, blockData, operations); + byte[] serializedTransaction = this.transferOperation.toBytes(); + System.out.println("Serialized transferOperation"); System.out.println(Util.bytesToHex(serializedTransaction)); } @@ -340,7 +335,7 @@ public class Test { public void testTransactionSerialization() { try { - Transaction transaction = new TransferTransactionBuilder() + TransferOperation transferOperation = new TransferTransactionBuilder() .setSource(new UserAccount("1.2.138632")) .setDestination(new UserAccount("1.2.129848")) .setAmount(new AssetAmount(UnsignedLong.valueOf(100), new Asset("1.3.120"))) @@ -350,9 +345,9 @@ public class Test { .build(); ArrayList transactionList = new ArrayList<>(); - transactionList.add(transaction); + transactionList.add(transferOperation); - byte[] signature = transaction.getGrapheneSignature(); + byte[] signature = transferOperation.getGrapheneSignature(); System.out.println(Util.bytesToHex(signature)); ApiCall call = new ApiCall(4, "call", "broadcast_transaction", transactionList, "2.0", 1); String jsonCall = call.toJsonString(); @@ -425,7 +420,7 @@ public class Test { }; try { - Transaction transaction = new TransferTransactionBuilder() + TransferOperation transferOperation = new TransferTransactionBuilder() .setSource(new UserAccount("1.2.138632")) .setDestination(new UserAccount("1.2.129848")) .setAmount(new AssetAmount(UnsignedLong.valueOf(100), new Asset("1.3.120"))) @@ -435,9 +430,9 @@ public class Test { .build(); ArrayList transactionList = new ArrayList<>(); - transactionList.add(transaction); + transactionList.add(transferOperation); - transactionList.add(transaction); + transactionList.add(transferOperation); SSLContext context = null; context = NaiveSSLContext.getInstance("TLS"); @@ -448,7 +443,7 @@ public class Test { WebSocket mWebSocket = factory.createSocket(OPENLEDGER_WITNESS_URL); - mWebSocket.addListener(new TransactionBroadcastSequence(transaction, listener)); + mWebSocket.addListener(new TransactionBroadcastSequence(transferOperation, listener)); mWebSocket.connect(); } catch (MalformedTransactionException e) { @@ -474,14 +469,29 @@ public class Test { } public void testPrivateKeyManipulations() { - ECKey privateKey = DumpedPrivateKey.fromBase58(null, Main.WIF).getKey(); + String brainKeyWords = "UNMATE AURIGAL NAVET WAVICLE REWOVE ABBOTCY COWHERB OUTKICK STOPPER JUSSORY BEAMLET WIRY"; + BrainKey brainKey = new BrainKey(brainKeyWords, 0); + + ECKey privateKey = DumpedPrivateKey.fromBase58(null, brainKey.getWalletImportFormat()).getKey(); System.out.println("private key..............: " + Util.bytesToHex(privateKey.getSecretBytes())); 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("base58...................: " + Base58.encode(privateKey.getPubKeyPoint().getEncoded(true))); System.out.println("base58...................: " + Base58.encode(privateKey.getPubKey())); - String brainKeyWords = "PUMPER ISOTOME SERE STAINER CLINGER MOONLIT CHAETA UPBRIM AEDILIC BERTHER NIT SHAP SAID SHADING JUNCOUS CHOUGH"; - BrainKey brainKey = new BrainKey(brainKeyWords, 0); + } + + 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() { diff --git a/src/main/java/com/luminiasoft/bitshares/TransactionBuilder.java b/src/main/java/com/luminiasoft/bitshares/TransactionBuilder.java index 05249a8..4d2e1ac 100644 --- a/src/main/java/com/luminiasoft/bitshares/TransactionBuilder.java +++ b/src/main/java/com/luminiasoft/bitshares/TransactionBuilder.java @@ -11,5 +11,5 @@ public abstract class TransactionBuilder { protected ECKey privateKey; protected BlockData blockData; - public abstract Transaction build() throws MalformedTransactionException; + public abstract TransferOperation build() throws MalformedTransactionException; } diff --git a/src/main/java/com/luminiasoft/bitshares/Transaction.java b/src/main/java/com/luminiasoft/bitshares/TransferOperation.java similarity index 89% rename from src/main/java/com/luminiasoft/bitshares/Transaction.java rename to src/main/java/com/luminiasoft/bitshares/TransferOperation.java index 58ad0b8..ad6e8f4 100644 --- a/src/main/java/com/luminiasoft/bitshares/Transaction.java +++ b/src/main/java/com/luminiasoft/bitshares/TransferOperation.java @@ -25,7 +25,7 @@ import java.util.TimeZone; /** * Class used to represent a generic graphene transaction. */ -public class Transaction implements ByteSerializable, JsonSerializable { +public class TransferOperation extends BaseOperation implements ByteSerializable, JsonSerializable { private final String TAG = this.getClass().getName(); public static final String KEY_EXPIRATION = "expiration"; @@ -41,12 +41,13 @@ public class Transaction implements ByteSerializable, JsonSerializable { private List extensions; /** - * Transaction constructor. + * TransferOperation constructor. * @param wif: The user's private key in the base58 format. * @param block_data: Block data containing important information used to sign a transaction. * @param operation_list: List of operations to include in the transaction. */ - public Transaction(String wif, BlockData block_data, List operation_list){ + public TransferOperation(String wif, BlockData block_data, List operation_list){ + super(OperationType.transfer_operation); this.privateKey = DumpedPrivateKey.fromBase58(null, wif).getKey(); this.blockData = block_data; this.operations = operation_list; @@ -54,12 +55,13 @@ public class Transaction implements ByteSerializable, JsonSerializable { } /** - * Transaction constructor. + * TransferOperation constructor. * @param privateKey : Instance of a ECKey containing the private key that will be used to sign this transaction. * @param blockData : Block data containing important information used to sign a transaction. * @param operationList : List of operations to include in the transaction. */ - public Transaction(ECKey privateKey, BlockData blockData, List operationList){ + public TransferOperation(ECKey privateKey, BlockData blockData, List operationList){ + super(OperationType.transfer_operation); this.privateKey = privateKey; this.blockData = blockData; this.operations = operationList; @@ -117,6 +119,12 @@ public class Transaction implements ByteSerializable, JsonSerializable { } return sigData; } + + @Override + public byte getId() { + return 0; + } + /** * Method that creates a serialized byte array with compact information about this transaction * that is needed for the creation of a signature. @@ -157,7 +165,7 @@ public class Transaction implements ByteSerializable, JsonSerializable { @Override public String toJsonString() { GsonBuilder gsonBuilder = new GsonBuilder(); - gsonBuilder.registerTypeAdapter(Transaction.class, new TransactionSerializer()); + gsonBuilder.registerTypeAdapter(TransferOperation.class, new TransactionSerializer()); return gsonBuilder.create().toJson(this); } @@ -200,11 +208,11 @@ public class Transaction implements ByteSerializable, JsonSerializable { } - class TransactionSerializer implements JsonSerializer { + class TransactionSerializer implements JsonSerializer { @Override - public JsonElement serialize(Transaction transaction, Type type, JsonSerializationContext jsonSerializationContext) { - return transaction.toJsonObject(); + public JsonElement serialize(TransferOperation transferOperation, Type type, JsonSerializationContext jsonSerializationContext) { + return transferOperation.toJsonObject(); } } } \ No newline at end of file diff --git a/src/main/java/com/luminiasoft/bitshares/TransferTransactionBuilder.java b/src/main/java/com/luminiasoft/bitshares/TransferTransactionBuilder.java index 61f046c..8ea9ac7 100644 --- a/src/main/java/com/luminiasoft/bitshares/TransferTransactionBuilder.java +++ b/src/main/java/com/luminiasoft/bitshares/TransferTransactionBuilder.java @@ -54,7 +54,7 @@ public class TransferTransactionBuilder extends TransactionBuilder { } @Override - public Transaction build() throws MalformedTransactionException { + public TransferOperation build() throws MalformedTransactionException { if(privateKey == null){ throw new MalformedTransactionException("Missing private key information"); }else if(blockData == null){ @@ -81,6 +81,6 @@ public class TransferTransactionBuilder extends TransactionBuilder { } operations.add(transferOperation); } - return new Transaction(privateKey, blockData, operations); + return new TransferOperation(privateKey, blockData, operations); } } diff --git a/src/main/java/com/luminiasoft/bitshares/errors/MalformedAddressException.java b/src/main/java/com/luminiasoft/bitshares/errors/MalformedAddressException.java new file mode 100644 index 0000000..a1c673d --- /dev/null +++ b/src/main/java/com/luminiasoft/bitshares/errors/MalformedAddressException.java @@ -0,0 +1,10 @@ +package com.luminiasoft.bitshares.errors; + +/** + * Created by nelson on 12/1/16. + */ +public class MalformedAddressException extends Exception { + public MalformedAddressException(String message){ + super(message); + } +} diff --git a/src/main/java/com/luminiasoft/bitshares/ws/TransactionBroadcastSequence.java b/src/main/java/com/luminiasoft/bitshares/ws/TransactionBroadcastSequence.java index fa1a312..bbd6b68 100644 --- a/src/main/java/com/luminiasoft/bitshares/ws/TransactionBroadcastSequence.java +++ b/src/main/java/com/luminiasoft/bitshares/ws/TransactionBroadcastSequence.java @@ -2,12 +2,8 @@ package com.luminiasoft.bitshares.ws; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; -import com.luminiasoft.bitshares.BaseOperation; -import com.luminiasoft.bitshares.BlockData; -import com.luminiasoft.bitshares.RPC; -import com.luminiasoft.bitshares.Transaction; -import com.luminiasoft.bitshares.Transfer; -import com.luminiasoft.bitshares.TransferTransactionBuilder; +import com.luminiasoft.bitshares.*; +import com.luminiasoft.bitshares.TransferOperation; import com.luminiasoft.bitshares.interfaces.WitnessResponseListener; import com.luminiasoft.bitshares.models.ApiCall; import com.luminiasoft.bitshares.models.BaseResponse; @@ -28,7 +24,7 @@ import java.util.Map; import java.util.TimeZone; /** - * Class that will handle the transaction publication procedure. + * Class that will handle the transferOperation publication procedure. */ public class TransactionBroadcastSequence extends WebSocketAdapter { private final String TAG = this.getClass().getName(); @@ -39,7 +35,7 @@ public class TransactionBroadcastSequence extends WebSocketAdapter { private final static int BROADCAST_TRANSACTION = 4; public final static int EXPIRATION_TIME = 30; - private Transaction transaction; + private TransferOperation transferOperation; private long expirationTime; private String headBlockId; private long headBlockNumber; @@ -51,13 +47,13 @@ public class TransactionBroadcastSequence extends WebSocketAdapter { /** * Constructor of this class. The ids required - * @param transaction: The transaction to be broadcasted. + * @param transferOperation: The transferOperation to be broadcasted. * @param listener: A class implementing the WitnessResponseListener interface. This should * be implemented by the party interested in being notified about the success/failure - * of the transaction broadcast operation. + * of the transferOperation broadcast operation. */ - public TransactionBroadcastSequence(Transaction transaction, WitnessResponseListener listener){ - this.transaction = transaction; + public TransactionBroadcastSequence(TransferOperation transferOperation, WitnessResponseListener listener){ + this.transferOperation = transferOperation; this.mListener = listener; } @@ -106,14 +102,14 @@ public class TransactionBroadcastSequence extends WebSocketAdapter { headBlockNumber = dynamicProperties.head_block_number; ArrayList transactionList = new ArrayList<>(); - transactionList.add(transaction); + transactionList.add(transferOperation); ApiCall call = new ApiCall(broadcastApiId, RPC.CALL_BROADCAST_TRANSACTION, transactionList, "2.0", currentId); - // Finally sending transaction + // Finally sending transferOperation websocket.sendText(call.toJsonString()); }else if(baseResponse.id >= BROADCAST_TRANSACTION){ Type WitnessResponseType = new TypeToken>(){}.getType(); @@ -135,18 +131,18 @@ public class TransactionBroadcastSequence extends WebSocketAdapter { * with ONE transfer operation. */ retries++; - List operations = this.transaction.getOperations(); + List operations = this.transferOperation.getOperations(); Transfer transfer = (Transfer) operations.get(0); - transaction = new TransferTransactionBuilder() + transferOperation = new TransferTransactionBuilder() .setSource(transfer.getFrom()) .setDestination(transfer.getTo()) .setAmount(transfer.getAmount()) .setFee(transfer.getFee()) .setBlockData(new BlockData(headBlockNumber, headBlockId, expirationTime + EXPIRATION_TIME)) - .setPrivateKey(transaction.getPrivateKey()) + .setPrivateKey(transferOperation.getPrivateKey()) .build(); ArrayList transactionList = new ArrayList<>(); - transactionList.add(transaction); + transactionList.add(transferOperation); ApiCall call = new ApiCall(broadcastApiId, RPC.CALL_BROADCAST_TRANSACTION, transactionList,