diff --git a/src/main/java/com/luminiasoft/bitshares/AccountOptions.java b/src/main/java/com/luminiasoft/bitshares/AccountOptions.java new file mode 100644 index 0000000..972a133 --- /dev/null +++ b/src/main/java/com/luminiasoft/bitshares/AccountOptions.java @@ -0,0 +1,134 @@ +package com.luminiasoft.bitshares; + +import com.google.common.primitives.Bytes; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.luminiasoft.bitshares.interfaces.GrapheneSerializable; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by nelson on 12/5/16. + */ +public class AccountOptions implements GrapheneSerializable { + public static final String KEY_MEMO_KEY = "memo_key"; + public static final String KEY_NUM_COMMITTEE = "num_committee"; + public static final String KEY_NUM_WITNESS = "num_witness"; + public static final String KEY_VOTES = "votes"; + public static final String KEY_VOTING_ACCOUNT = "voting_account"; + public static final String KEY_EXTENSIONS = Extensions.KEY_EXTENSIONS; + + private PublicKey memo_key; + private UserAccount voting_account; + private int num_witness; + private int num_comittee; + private Vote[] votes; + private Extensions extensions; + + public AccountOptions(){ + voting_account = new UserAccount(UserAccount.PROXY_TO_SELF); + this.votes = new Vote[0]; + this.extensions = new Extensions(); + } + + public AccountOptions(PublicKey memoKey){ + this(); + this.memo_key = memoKey; + } + + public PublicKey getMemoKey() { + return memo_key; + } + + public void setMemoKey(PublicKey memo_key) { + this.memo_key = memo_key; + } + + public UserAccount getVotingAccount() { + return voting_account; + } + + public void setVotingAccount(UserAccount voting_account) { + this.voting_account = voting_account; + } + + public int getNumWitness() { + return num_witness; + } + + public void setNumWitness(int num_witness) { + this.num_witness = num_witness; + } + + public int getNumComittee() { + return num_comittee; + } + + public void setNum_comittee(int num_comittee) { + this.num_comittee = num_comittee; + } + + public Vote[] getVotes() { + return votes; + } + + public void setVotes(Vote[] votes) { + this.votes = votes; + } + + @Override + public byte[] toBytes() { + List byteArray = new ArrayList(); + + if(memo_key != null){ + // Adding byte to indicate that there is memo data + byteArray.add((byte) 1); + + // Adding memo key + byteArray.addAll(Bytes.asList(memo_key.toBytes())); + + // Adding voting account + byteArray.addAll(Bytes.asList(voting_account.toBytes())); + + // Adding num_witness + byteArray.addAll(Bytes.asList(Util.revertShort(Short.valueOf((short) num_witness)))); + + // Adding num_committee + byteArray.addAll(Bytes.asList(Util.revertShort(Short.valueOf((short) num_comittee)))); + + // Vote's array length + byteArray.add((byte) votes.length); + + for(Vote vote : votes){ + //TODO: Check this serialization + byteArray.addAll(Bytes.asList(vote.toBytes())); + } + }else{ + byteArray.add((byte) 0); + } + return Bytes.toArray(byteArray); + } + + @Override + public String toJsonString() { + return null; + } + + @Override + public JsonElement toJsonObject() { + JsonObject options = new JsonObject(); + options.addProperty(KEY_MEMO_KEY, new Address(memo_key.getKey()).toString()); + options.addProperty(KEY_NUM_COMMITTEE, num_comittee); + options.addProperty(KEY_NUM_WITNESS, num_witness); + options.addProperty(KEY_VOTING_ACCOUNT, voting_account.getObjectId()); + JsonArray votesArray = new JsonArray(); + for(Vote vote : votes){ + //TODO: Add votes representation + } + options.add(KEY_VOTES, votesArray); + options.add(KEY_EXTENSIONS, extensions.toJsonObject()); + return options; + } +} diff --git a/src/main/java/com/luminiasoft/bitshares/AccountUpdateOperation.java b/src/main/java/com/luminiasoft/bitshares/AccountUpdateOperation.java index 8e7adb3..c716e1c 100644 --- a/src/main/java/com/luminiasoft/bitshares/AccountUpdateOperation.java +++ b/src/main/java/com/luminiasoft/bitshares/AccountUpdateOperation.java @@ -6,6 +6,8 @@ import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.sun.istack.internal.NotNull; +import com.sun.istack.internal.Nullable; /** * Class used to encapsulate operations related to the account_update_operation. @@ -15,25 +17,36 @@ public class AccountUpdateOperation extends BaseOperation { public static final String KEY_OWNER = "owner"; public static final String KEY_ACTIVE = "active"; public static final String KEY_FEE = "fee"; + public static final String KEY_NEW_OPTIONS = "new_options"; public static final String KEY_EXTENSIONS = "extensions"; - private UserAccount account; private AssetAmount fee; - private Authority owner; - private Authority active; + private UserAccount account; + private Optional owner; + private Optional active; + private Optional new_options; private Extensions extensions; - public AccountUpdateOperation(UserAccount account, Authority owner, Authority active, AssetAmount fee){ + /** + * Account update operation constructor. + * @param account User account to update. Can't be null. + * @param owner Owner authority to set. Can be null. + * @param active Active authority to set. Can be null. + * @param options Active authority to set. Can be null. + * @param fee The fee to pay. Can be null. + */ + public AccountUpdateOperation(UserAccount account, Authority owner, Authority active, AccountOptions options, AssetAmount fee){ super(OperationType.account_update_operation); - this.account = account; - this.owner = owner; - this.active = active; this.fee = fee; + this.account = account; + this.owner = new Optional<>(owner); + this.active = new Optional<>(active); + this.new_options = new Optional<>(options); extensions = new Extensions(); } - public AccountUpdateOperation(UserAccount account, Authority owner, Authority active){ - this(account, owner, active, new AssetAmount(UnsignedLong.valueOf(0), new Asset("1.3.0"))); + public AccountUpdateOperation(UserAccount account, Authority owner, Authority active, AccountOptions options){ + this(account, owner, active, options, new AssetAmount(UnsignedLong.valueOf(0), new Asset("1.3.0"))); } @Override @@ -41,6 +54,18 @@ public class AccountUpdateOperation extends BaseOperation { this.fee = fee; } + public void setOwner(Authority owner){ + this.owner = new Optional<>(owner); + } + + public void setActive(Authority active){ + this.active = new Optional<>(active); + } + + public void setAccountOptions(AccountOptions options){ + this.new_options = new Optional<>(options); + } + @Override public String toJsonString() { Gson gson = new Gson(); @@ -57,6 +82,7 @@ public class AccountUpdateOperation extends BaseOperation { accountUpdate.addProperty(KEY_ACCOUNT, account.toJsonString()); accountUpdate.add(KEY_OWNER, owner.toJsonObject()); accountUpdate.add(KEY_ACTIVE, active.toJsonObject()); + accountUpdate.add(KEY_NEW_OPTIONS, new_options.toJsonObject()); accountUpdate.add(KEY_EXTENSIONS, extensions.toJsonObject()); array.add(accountUpdate); return array; @@ -68,7 +94,8 @@ public class AccountUpdateOperation extends BaseOperation { byte[] accountBytes = account.toBytes(); byte[] ownerBytes = owner.toBytes(); byte[] activeBytes = active.toBytes(); + byte[] newOptionsBytes = new_options.toBytes(); byte[] extensionBytes = extensions.toBytes(); - return Bytes.concat(feeBytes, accountBytes, ownerBytes, activeBytes, extensionBytes); + return Bytes.concat(feeBytes, accountBytes, ownerBytes, activeBytes, newOptionsBytes, extensionBytes); } } diff --git a/src/main/java/com/luminiasoft/bitshares/AccountUpdateTransactionBuilder.java b/src/main/java/com/luminiasoft/bitshares/AccountUpdateTransactionBuilder.java index ecec78e..435f8a3 100644 --- a/src/main/java/com/luminiasoft/bitshares/AccountUpdateTransactionBuilder.java +++ b/src/main/java/com/luminiasoft/bitshares/AccountUpdateTransactionBuilder.java @@ -1,10 +1,66 @@ package com.luminiasoft.bitshares; +import com.luminiasoft.bitshares.errors.MalformedTransactionException; +import org.bitcoinj.core.ECKey; + +import java.util.ArrayList; import java.util.List; /** - * Created by nelson on 12/3/16. + * Class used to build a transaction containing an account update operation. */ -public class AccountUpdateTransactionBuilder { - private List operations; +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/src/main/java/com/luminiasoft/bitshares/Authority.java b/src/main/java/com/luminiasoft/bitshares/Authority.java index 646209f..a3637af 100644 --- a/src/main/java/com/luminiasoft/bitshares/Authority.java +++ b/src/main/java/com/luminiasoft/bitshares/Authority.java @@ -5,16 +5,14 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.luminiasoft.bitshares.errors.MalformedAddressException; -import com.luminiasoft.bitshares.interfaces.ByteSerializable; -import com.luminiasoft.bitshares.interfaces.JsonSerializable; +import com.luminiasoft.bitshares.interfaces.GrapheneSerializable; -import java.nio.ByteBuffer; import java.util.*; /** * Created by nelson on 11/30/16. */ -public class Authority implements JsonSerializable, ByteSerializable { +public class Authority implements GrapheneSerializable { public static final String KEY_ACCOUNT_AUTHS = "account_auths"; public static final String KEY_KEY_AUTHS = "key_auths"; public static final String KEY_WEIGHT_THRESHOLD = "weight_threshold"; @@ -25,17 +23,41 @@ public class Authority implements JsonSerializable, ByteSerializable { private HashMap key_auths; private Extensions extensions; - public Authority(long weight_threshold, HashMap keyAuths) throws MalformedAddressException { - this.weight_threshold = weight_threshold; - key_auths = new HashMap(); - for(String key : keyAuths.keySet()){ - Address address = new Address(key); - key_auths.put(address.getPublicKey(), keyAuths.get(key)); - } - account_auths = new HashMap(); + public Authority(){ + this.weight_threshold = 1; + this.account_auths = new HashMap(); + this.key_auths = new HashMap(); extensions = new Extensions(); } + /** + * Constructor for the authority class that takes every possible detail. + * @param weight_threshold: The total weight threshold + * @param keyAuths: Map of key to weights relationships. Can be null. + * @param accountAuths: Map of account to weights relationships. Can be null. + * @throws MalformedAddressException + */ + public Authority(long weight_threshold, HashMap keyAuths, HashMap accountAuths) { + this(); + this.weight_threshold = weight_threshold; + if(keyAuths != null) + this.key_auths = keyAuths; + if(accountAuths != null) + this.account_auths = accountAuths; + } + + public void setKeyAuthorities(HashMap keyAuths){ + if(keyAuths != null){ + for(Address address : keyAuths.keySet()){ + key_auths.put(address.getPublicKey(), keyAuths.get(address)); + } + } + } + + public void setAccountAuthorities(HashMap accountAuthorities){ + this.account_auths = accountAuthorities; + } + @Override public String toJsonString() { return null; @@ -74,25 +96,34 @@ public class Authority implements JsonSerializable, ByteSerializable { // Adding number of authorities byteArray.add(Byte.valueOf((byte) (account_auths.size() + key_auths.size()))); - // Weight threshold - byteArray.addAll(Bytes.asList(Util.revertInteger(new Integer((int) weight_threshold)))); + // If the authority is not empty of references, we serialize its contents + // otherwise its only contribution will be a zero byte + if(account_auths.size() + key_auths.size() > 0){ + // Weight threshold + byteArray.addAll(Bytes.asList(Util.revertInteger(new Integer((int) weight_threshold)))); - // Number of account authorities - byteArray.add((byte) account_auths.size()); + // Number of account authorities + byteArray.add((byte) account_auths.size()); - //TODO: Add account authorities + //TODO: Check the account authorities serialization + // Serializing individual accounts and their corresponding weights + for(UserAccount account : account_auths.keySet()){ + byteArray.addAll(Bytes.asList(account.toBytes())); + byteArray.addAll(Bytes.asList(Util.revertShort(account_auths.get(account).shortValue()))); + } - // Number of key authorities - byteArray.add((byte) key_auths.size()); + // Number of key authorities + byteArray.add((byte) key_auths.size()); - for(PublicKey publicKey : key_auths.keySet()){ - byteArray.addAll(Bytes.asList(publicKey.toBytes())); - byteArray.addAll(Bytes.asList(Util.revertShort(key_auths.get(publicKey).shortValue()))); + // Serializing individual keys and their corresponding weights + for(PublicKey publicKey : key_auths.keySet()){ + byteArray.addAll(Bytes.asList(publicKey.toBytes())); + byteArray.addAll(Bytes.asList(Util.revertShort(key_auths.get(publicKey).shortValue()))); + } + + // Adding number of extensions + byteArray.add((byte) extensions.size()); } - - // Adding number of extensions - byteArray.add((byte) extensions.size()); - return Bytes.toArray(byteArray); } -} +} \ No newline at end of file diff --git a/src/main/java/com/luminiasoft/bitshares/Extensions.java b/src/main/java/com/luminiasoft/bitshares/Extensions.java index 5481685..18c9a6d 100644 --- a/src/main/java/com/luminiasoft/bitshares/Extensions.java +++ b/src/main/java/com/luminiasoft/bitshares/Extensions.java @@ -11,6 +11,8 @@ import java.util.ArrayList; * Created by nelson on 11/9/16. */ public class Extensions implements JsonSerializable, ByteSerializable { + public static final String KEY_EXTENSIONS = "extensions"; + private ArrayList extensions; public Extensions(){ diff --git a/src/main/java/com/luminiasoft/bitshares/Main.java b/src/main/java/com/luminiasoft/bitshares/Main.java index 2fb7685..171e15a 100644 --- a/src/main/java/com/luminiasoft/bitshares/Main.java +++ b/src/main/java/com/luminiasoft/bitshares/Main.java @@ -11,6 +11,8 @@ public class Main { public static final String BILTHON_5_BRAIN_KEY = "UNMATE AURIGAL NAVET WAVICLE REWOVE ABBOTCY COWHERB OUTKICK STOPPER JUSSORY BEAMLET WIRY"; + public static final String BILTHON_7_BRAIN_KEY = "VENIN QUOTHA OBESELY TORIC OSMATIC SPOKEN DIACOPE CUBICA TABULA REDDING APONIA TARTAR"; + //public static final String BRAIN_KEY = "TWIXT SERMO TRILLI AUDIO PARDED PLUMET BIWA REHUNG MAUDLE VALVULA OUTBURN FEWNESS ALIENER UNTRACE PRICH TROKER"; //public static final String BRAIN_KEY = "SIVER TIKKER FOGO HOMINAL PRAYER LUTEIN SMALLY ACARID MEROPIA TRANCE BOGONG IDDAT HICKORY SOUTANE MOOD DOWSER"; public static final String BIP39_KEY = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; @@ -55,7 +57,7 @@ public class Main { // test.testGetAccountByName(); // test.testGetRequiredFees(); // test.testRandomNumberGeneration(); -// test.testBrainKeyOperations(false); +// test.testBrainKeyOperations(true); // test.testBip39Opertion(); // test.testAccountNamebyAddress(); // test.testAccountNameById(); diff --git a/src/main/java/com/luminiasoft/bitshares/Optional.java b/src/main/java/com/luminiasoft/bitshares/Optional.java new file mode 100644 index 0000000..f7cc88f --- /dev/null +++ b/src/main/java/com/luminiasoft/bitshares/Optional.java @@ -0,0 +1,33 @@ +package com.luminiasoft.bitshares; + +import com.google.gson.JsonElement; +import com.luminiasoft.bitshares.interfaces.GrapheneSerializable; + +/** + * Used whenever we have an optional field. + */ +public class Optional implements GrapheneSerializable { + private T optionalField; + + public Optional(T field){ + optionalField = field; + } + + @Override + public byte[] toBytes() { + if(optionalField == null) + return new byte[] { (byte) 0 }; + else + return optionalField.toBytes(); + } + + @Override + public String toJsonString() { + return optionalField.toJsonString(); + } + + @Override + public JsonElement toJsonObject() { + return optionalField.toJsonObject(); + } +} diff --git a/src/main/java/com/luminiasoft/bitshares/Test.java b/src/main/java/com/luminiasoft/bitshares/Test.java index afedc93..d623b96 100644 --- a/src/main/java/com/luminiasoft/bitshares/Test.java +++ b/src/main/java/com/luminiasoft/bitshares/Test.java @@ -341,8 +341,8 @@ public class Test { .setDestination(new UserAccount("1.2.129848")) .setAmount(new AssetAmount(UnsignedLong.valueOf(100), new Asset("1.3.120"))) .setFee(new AssetAmount(UnsignedLong.valueOf(264174), new Asset("1.3.0"))) - .setBlockData(new BlockData(Main.REF_BLOCK_NUM, Main.REF_BLOCK_PREFIX, Main.RELATIVE_EXPIRATION)) .setPrivateKey(DumpedPrivateKey.fromBase58(null, Main.WIF).getKey()) + .setBlockData(new BlockData(Main.REF_BLOCK_NUM, Main.REF_BLOCK_PREFIX, Main.RELATIVE_EXPIRATION)) .build(); ArrayList transactionList = new ArrayList<>(); @@ -532,49 +532,6 @@ public class Test { } } - public void testRandomNumberGeneration() { - byte[] seed = new byte[]{new Long(System.nanoTime()).byteValue()}; - doCountTest(new SHA512Digest(), seed); - } - - private void doCountTest(Digest digest, byte[] seed)//, byte[] expectedXors) - { - DigestRandomGenerator generator = new DigestRandomGenerator(digest); - byte[] output = new byte[digest.getDigestSize()]; - int[] averages = new int[digest.getDigestSize()]; - byte[] ands = new byte[digest.getDigestSize()]; - byte[] xors = new byte[digest.getDigestSize()]; - byte[] ors = new byte[digest.getDigestSize()]; - - generator.addSeedMaterial(seed); - - for (int i = 0; i != 1000000; i++) { - generator.nextBytes(output); - for (int j = 0; j != output.length; j++) { - averages[j] += output[j] & 0xff; - ands[j] &= output[j]; - xors[j] ^= output[j]; - ors[j] |= output[j]; - } - } - - for (int i = 0; i != output.length; i++) { - if ((averages[i] / 1000000) != 127) { - System.out.println("average test failed for " + digest.getAlgorithmName()); - } - System.out.println("averages[" + i + "] / 1000000: " + averages[i] / 1000000); - if (ands[i] != 0) { - System.out.println("and test failed for " + digest.getAlgorithmName()); - } - if ((ors[i] & 0xff) != 0xff) { - System.out.println("or test failed for " + digest.getAlgorithmName()); - } -// if (xors[i] != expectedXors[i]) { -// System.out.println("xor test failed for " + digest.getAlgorithmName()); -// } - } - } - /** * The final purpose of this test is to convert the plain brainkey at * Main.BRAIN_KEY into the WIF at Main.WIF @@ -595,29 +552,21 @@ public class Test { brainKey = new BrainKey(Main.BILTHON_5_BRAIN_KEY, 0); } ECKey key = brainKey.getPrivateKey(); - System.out.println("Private key"); - System.out.println(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); + 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 Decompressed.............: " + wif2); byte[] pubKey1 = key.decompress().getPubKey(); - System.out.println("decompressed public key: " + Base58.encode(pubKey1)); byte[] pubKey2 = key.getPubKey(); - System.out.println("compressed public key: " + Base58.encode(pubKey2)); - System.out.println("pub key compressed : " + Util.bytesToHex(pubKey1)); - System.out.println("pub key uncompressed : " + Util.bytesToHex(pubKey2)); - - byte[] pubKey3 = key.getPubKeyPoint().getEncoded(true); - System.out.println("pub key compressed : " + Base58.encode(pubKey3)); + System.out.println("Public Key Decompressed........: " + Util.bytesToHex(pubKey1)); + System.out.println("Public Key Compressed..........: " + Util.bytesToHex(pubKey2)); // Address generation test Address address = new Address(key); - System.out.println("Block explorer's address: " + address); - - System.out.println("Wif: : " + brainKey.getWalletImportFormat()); + System.out.println("Block explorer's address.....: " + address); } catch (FileNotFoundException e) { System.out.println("FileNotFoundException. Msg: " + e.getMessage()); } catch (IOException e) { @@ -744,20 +693,30 @@ public class Test { } public void testAccountUpdateSerialization() { - UserAccount account = new UserAccount("1.2.138632"); - AssetAmount fee = new AssetAmount(UnsignedLong.valueOf("200"), new Asset("1.3.0")); - HashMap keyAuths = new HashMap<>(); - keyAuths.put("BTS8RiFgs8HkcVPVobHLKEv6yL3iXcC9SWjbPVS15dDAXLG9GYhnY", 1); + String newAddress = "BTS8RiFgs8HkcVPVobHLKEv6yL3iXcC9SWjbPVS15dDAXLG9GYhnY"; try { - BlockData blockData = new BlockData(0, 0, 0); - Authority owner = new Authority(1, keyAuths); - Authority active = new Authority(1, keyAuths); - AccountUpdateOperation operation = new AccountUpdateOperation(account, owner, active, fee); - ArrayList operations = new ArrayList(); - operations.add(operation); - Transaction transaction = new Transaction(Main.WIF, blockData, operations); + 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_7_BRAIN_KEY, 0); + Transaction transaction = new AccountUpdateTransactionBuilder(brainKey.getPrivateKey()) + .setAccont(new UserAccount("1.2.140994")) + .setOwner(authority) + .setActive(authority) + .setOptions(options) + .setBlockData(new BlockData(Main.REF_BLOCK_NUM, Main.REF_BLOCK_PREFIX, Main.RELATIVE_EXPIRATION)) + .build(); + + 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 (MalformedTransactionException e) { + System.out.println("MalformedTransactionException. Msg: "+e.getMessage()); } } @@ -775,18 +734,20 @@ public class Test { } }; - UserAccount account = new UserAccount("1.2.139313"); - AssetAmount fee = new AssetAmount(UnsignedLong.valueOf("200"), new Asset("1.3.0")); - HashMap keyAuths = new HashMap<>(); - keyAuths.put("BTS8RiFgs8HkcVPVobHLKEv6yL3iXcC9SWjbPVS15dDAXLG9GYhnY", 1); + String newAddress = "BTS8RiFgs8HkcVPVobHLKEv6yL3iXcC9SWjbPVS15dDAXLG9GYhnY"; try { - Authority owner = new Authority(1, keyAuths); - Authority active = new Authority(1, keyAuths); - AccountUpdateOperation operation = new AccountUpdateOperation(account, owner, active, fee); - ArrayList operations = new ArrayList(); - operations.add(operation); - BrainKey brainKey = new BrainKey(Main.BILTHON_5_BRAIN_KEY, 0); - Transaction transaction = new Transaction(brainKey.getWalletImportFormat(), null, operations); + 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_7_BRAIN_KEY, 0); + Transaction transaction = new AccountUpdateTransactionBuilder(brainKey.getPrivateKey()) + .setAccont(new UserAccount("1.2.140994")) + .setOwner(authority) + .setActive(authority) + .setOptions(options) + .build(); SSLContext context = null; context = NaiveSSLContext.getInstance("TLS"); @@ -799,8 +760,11 @@ public class Test { 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) { @@ -808,5 +772,42 @@ public class Test { } catch (WebSocketException e) { System.out.println("WebSocketException. Msg: "+e.getMessage()); } + + + // -- + +// UserAccount account = new UserAccount("1.2.139313"); +// AssetAmount fee = new AssetAmount(UnsignedLong.valueOf("200"), new Asset("1.3.0")); +// HashMap keyAuths = new HashMap<>(); +// keyAuths.put("BTS8RiFgs8HkcVPVobHLKEv6yL3iXcC9SWjbPVS15dDAXLG9GYhnY", 1); +// try { +// Authority owner = new Authority(1, keyAuths); +// Authority active = new Authority(1, keyAuths); +// AccountUpdateOperation operation = new AccountUpdateOperation(account, owner, active, null, fee); +// ArrayList operations = new ArrayList(); +// operations.add(operation); +// BrainKey brainKey = new BrainKey(Main.BILTHON_5_BRAIN_KEY, 0); +// Transaction transaction = new Transaction(brainKey.getWalletImportFormat(), null, operations); +// +// 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); +// +// mWebSocket.addListener(new TransactionBroadcastSequence(transaction, new Asset("1.3.0"), listener)); +// mWebSocket.connect(); +// } catch (MalformedAddressException e) { +// System.out.println("MalformedAddressException. 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()); +// } } } diff --git a/src/main/java/com/luminiasoft/bitshares/Transaction.java b/src/main/java/com/luminiasoft/bitshares/Transaction.java index 967d5a4..29a4620 100644 --- a/src/main/java/com/luminiasoft/bitshares/Transaction.java +++ b/src/main/java/com/luminiasoft/bitshares/Transaction.java @@ -24,7 +24,7 @@ import java.util.List; import java.util.TimeZone; /** - * Class used to represent a generic graphene transaction. + * Class used to represent a generic Graphene transaction. */ public class Transaction implements ByteSerializable, JsonSerializable { private final String TAG = this.getClass().getName(); @@ -65,10 +65,18 @@ public class Transaction implements ByteSerializable, JsonSerializable { this(DumpedPrivateKey.fromBase58(null, wif).getKey(), block_data, operation_list); } + /** + * Updates the block data + * @param blockData: New block data + */ public void setBlockData(BlockData blockData){ this.blockData = blockData; } + /** + * Updates the fees for all operations in this transaction. + * @param fees: New fees to apply + */ public void setFees(List fees){ for(int i = 0; i < operations.size(); i++) operations.get(i).setFee(fees.get(i)); diff --git a/src/main/java/com/luminiasoft/bitshares/TransactionBuilder.java b/src/main/java/com/luminiasoft/bitshares/TransactionBuilder.java index 05249a8..068a23c 100644 --- a/src/main/java/com/luminiasoft/bitshares/TransactionBuilder.java +++ b/src/main/java/com/luminiasoft/bitshares/TransactionBuilder.java @@ -11,5 +11,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/src/main/java/com/luminiasoft/bitshares/TransferTransactionBuilder.java b/src/main/java/com/luminiasoft/bitshares/TransferTransactionBuilder.java index 0caf11e..c3ada97 100644 --- a/src/main/java/com/luminiasoft/bitshares/TransferTransactionBuilder.java +++ b/src/main/java/com/luminiasoft/bitshares/TransferTransactionBuilder.java @@ -16,6 +16,12 @@ public class TransferTransactionBuilder extends TransactionBuilder { private AssetAmount transferAmount; private AssetAmount feeAmount; + public TransferTransactionBuilder(){} + + public TransferTransactionBuilder(ECKey privKey) { + super(privKey); + } + public TransferTransactionBuilder setPrivateKey(ECKey key){ this.privateKey = key; return this; diff --git a/src/main/java/com/luminiasoft/bitshares/UserAccount.java b/src/main/java/com/luminiasoft/bitshares/UserAccount.java index a1dba3e..e25c261 100644 --- a/src/main/java/com/luminiasoft/bitshares/UserAccount.java +++ b/src/main/java/com/luminiasoft/bitshares/UserAccount.java @@ -15,6 +15,8 @@ import java.io.IOException; */ public class UserAccount extends GrapheneObject implements ByteSerializable, JsonSerializable { + public static final String PROXY_TO_SELF = "1.2.5"; + /** * Constructor that expects a user account in the string representation. * That is in the 1.2.x format. diff --git a/src/main/java/com/luminiasoft/bitshares/Vote.java b/src/main/java/com/luminiasoft/bitshares/Vote.java new file mode 100644 index 0000000..d8f674f --- /dev/null +++ b/src/main/java/com/luminiasoft/bitshares/Vote.java @@ -0,0 +1,33 @@ +package com.luminiasoft.bitshares; + +import com.luminiasoft.bitshares.interfaces.ByteSerializable; + +/** + * Created by nelson on 12/5/16. + */ +public class Vote implements ByteSerializable { + private int type; + private int instance; + + public Vote(String vote){ + String[] parts = vote.split(":"); + assert(parts.length == 2); + this.type = Integer.valueOf(parts[0]); + this.instance = Integer.valueOf(parts[1]); + } + + public Vote(int type, int instance){ + this.type = type; + this.instance = instance; + } + + @Override + public String toString() { + return String.format("%d:%d", this.type, this.instance); + } + + @Override + public byte[] toBytes() { + return new byte[] { (byte) this.instance, (byte) this.type }; + } +} diff --git a/src/main/java/com/luminiasoft/bitshares/interfaces/GrapheneSerializable.java b/src/main/java/com/luminiasoft/bitshares/interfaces/GrapheneSerializable.java new file mode 100644 index 0000000..68ed06f --- /dev/null +++ b/src/main/java/com/luminiasoft/bitshares/interfaces/GrapheneSerializable.java @@ -0,0 +1,7 @@ +package com.luminiasoft.bitshares.interfaces; + +/** + * Interface used to group both ByteSerializable and JsonSerializable interfaces. + */ +public interface GrapheneSerializable extends ByteSerializable, JsonSerializable { +} diff --git a/src/main/java/com/luminiasoft/bitshares/interfaces/JsonSerializable.java b/src/main/java/com/luminiasoft/bitshares/interfaces/JsonSerializable.java index 78f8d29..e1f7377 100644 --- a/src/main/java/com/luminiasoft/bitshares/interfaces/JsonSerializable.java +++ b/src/main/java/com/luminiasoft/bitshares/interfaces/JsonSerializable.java @@ -5,7 +5,8 @@ import com.google.gson.JsonElement; import java.io.Serializable; /** - * Interface to be implemented by any entity for which makes sense to have a JSON-formatted string representation. + * Interface to be implemented by any entity for which makes sense to + * have a JSON-formatted string and object representation. */ public interface JsonSerializable extends Serializable { diff --git a/src/main/java/com/luminiasoft/bitshares/ws/TransactionBroadcastSequence.java b/src/main/java/com/luminiasoft/bitshares/ws/TransactionBroadcastSequence.java index 71202ba..ecd93b7 100644 --- a/src/main/java/com/luminiasoft/bitshares/ws/TransactionBroadcastSequence.java +++ b/src/main/java/com/luminiasoft/bitshares/ws/TransactionBroadcastSequence.java @@ -142,12 +142,17 @@ public class TransactionBroadcastSequence extends WebSocketAdapter { @Override public void onError(WebSocket websocket, WebSocketException cause) throws Exception { + System.out.println("onError. cause: "+cause.getMessage()); mListener.onError(new BaseResponse.Error(cause.getMessage())); websocket.disconnect(); } @Override public void handleCallbackError(WebSocket websocket, Throwable cause) throws Exception { + System.out.println("handleCallbackError. cause: "+cause.getMessage()+", error: "+cause.getClass()); + for (StackTraceElement element : cause.getStackTrace()){ + System.out.println(element.getFileName()+"#"+element.getClassName()+":"+element.getLineNumber()); + } mListener.onError(new BaseResponse.Error(cause.getMessage())); websocket.disconnect(); }