lookup accountName
This commit is contained in:
parent
d53f79106e
commit
8123f10521
28 changed files with 1035 additions and 401 deletions
134
src/main/java/com/luminiasoft/bitshares/AccountOptions.java
Normal file
134
src/main/java/com/luminiasoft/bitshares/AccountOptions.java
Normal file
|
@ -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<Byte> byteArray = new ArrayList<Byte>();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
package com.luminiasoft.bitshares;
|
||||||
|
|
||||||
|
import com.google.common.primitives.Bytes;
|
||||||
|
import com.google.common.primitives.UnsignedLong;
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
public class AccountUpdateOperation extends BaseOperation {
|
||||||
|
public static final String KEY_ACCOUNT = "account";
|
||||||
|
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 AssetAmount fee;
|
||||||
|
private UserAccount account;
|
||||||
|
private Optional<Authority> owner;
|
||||||
|
private Optional<Authority> active;
|
||||||
|
private Optional<AccountOptions> new_options;
|
||||||
|
private Extensions extensions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.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, AccountOptions options){
|
||||||
|
this(account, owner, active, options, new AssetAmount(UnsignedLong.valueOf(0), new Asset("1.3.0")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFee(AssetAmount fee){
|
||||||
|
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();
|
||||||
|
return gson.toJson(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonElement toJsonObject() {
|
||||||
|
JsonArray array = new JsonArray();
|
||||||
|
array.add(this.getId());
|
||||||
|
|
||||||
|
JsonObject accountUpdate = new JsonObject();
|
||||||
|
accountUpdate.add(KEY_FEE, fee.toJsonObject());
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] toBytes() {
|
||||||
|
byte[] feeBytes = fee.toBytes();
|
||||||
|
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, newOptionsBytes, extensionBytes);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class used to build a transaction containing an account update operation.
|
||||||
|
*/
|
||||||
|
public class AccountUpdateTransactionBuilder extends TransactionBuilder {
|
||||||
|
private List<BaseOperation> 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,57 +1,66 @@
|
||||||
package com.luminiasoft.bitshares;
|
package com.luminiasoft.bitshares;
|
||||||
|
|
||||||
import com.google.common.primitives.Bytes;
|
import com.google.common.primitives.Bytes;
|
||||||
import com.google.gson.internal.LinkedTreeMap;
|
import com.luminiasoft.bitshares.errors.MalformedAddressException;
|
||||||
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 org.bitcoinj.core.Base58;
|
import org.bitcoinj.core.Base58;
|
||||||
import org.bitcoinj.core.ECKey;
|
import org.bitcoinj.core.ECKey;
|
||||||
import org.spongycastle.crypto.digests.RIPEMD160Digest;
|
import org.spongycastle.crypto.digests.RIPEMD160Digest;
|
||||||
|
import org.spongycastle.math.ec.ECPoint;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.IllegalFormatException;
|
||||||
import javafx.util.Pair;
|
|
||||||
import javax.net.ssl.SSLContext;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class used to encapsulate address-related operations.
|
* Class used to encapsulate address-related operations.
|
||||||
*/
|
*/
|
||||||
public class Address {
|
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;
|
private String prefix;
|
||||||
|
|
||||||
public Address(ECKey key) {
|
public Address(ECKey key) {
|
||||||
this.key = key;
|
this.publicKey = new PublicKey(key);
|
||||||
this.prefix = DEFAULT_PREFIX;
|
this.prefix = BITSHARES_PREFIX;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Address(ECKey key, String prefix) {
|
public Address(ECKey key, String prefix) {
|
||||||
this.key = key;
|
this.publicKey = new PublicKey(key);
|
||||||
this.prefix = prefix;
|
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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
byte[] pubKey = key.getPubKey();
|
byte[] pubKey = this.publicKey.toBytes();
|
||||||
byte[] checksum = new byte[160 / 8];
|
byte[] checksum = calculateChecksum(pubKey);
|
||||||
RIPEMD160Digest ripemd160Digest = new RIPEMD160Digest();
|
byte[] pubKeyChecksummed = Bytes.concat(pubKey, checksum);
|
||||||
ripemd160Digest.update(pubKey, 0, pubKey.length);
|
|
||||||
ripemd160Digest.doFinal(checksum, 0);
|
|
||||||
byte[] pubKeyChecksummed = Bytes.concat(pubKey, Arrays.copyOfRange(checksum, 0, 4));
|
|
||||||
return this.prefix + Base58.encode(pubKeyChecksummed);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
129
src/main/java/com/luminiasoft/bitshares/Authority.java
Normal file
129
src/main/java/com/luminiasoft/bitshares/Authority.java
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
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.errors.MalformedAddressException;
|
||||||
|
import com.luminiasoft.bitshares.interfaces.GrapheneSerializable;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by nelson on 11/30/16.
|
||||||
|
*/
|
||||||
|
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";
|
||||||
|
public static final String KEY_EXTENSIONS = "extensions";
|
||||||
|
|
||||||
|
private long weight_threshold;
|
||||||
|
private HashMap<UserAccount, Integer> account_auths;
|
||||||
|
private HashMap<PublicKey, Integer> key_auths;
|
||||||
|
private Extensions extensions;
|
||||||
|
|
||||||
|
public Authority(){
|
||||||
|
this.weight_threshold = 1;
|
||||||
|
this.account_auths = new HashMap<UserAccount, Integer>();
|
||||||
|
this.key_auths = new HashMap<PublicKey, Integer>();
|
||||||
|
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<PublicKey, Integer> keyAuths, HashMap<UserAccount, Integer> 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<Address, Integer> keyAuths){
|
||||||
|
if(keyAuths != null){
|
||||||
|
for(Address address : keyAuths.keySet()){
|
||||||
|
key_auths.put(address.getPublicKey(), keyAuths.get(address));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAccountAuthorities(HashMap<UserAccount, Integer> accountAuthorities){
|
||||||
|
this.account_auths = accountAuthorities;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toJsonString() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonElement toJsonObject() {
|
||||||
|
JsonObject authority = new JsonObject();
|
||||||
|
authority.addProperty(KEY_WEIGHT_THRESHOLD, weight_threshold);
|
||||||
|
JsonArray keyAuthArray = new JsonArray();
|
||||||
|
JsonArray accountAuthArray = new JsonArray();
|
||||||
|
|
||||||
|
for(PublicKey publicKey : key_auths.keySet()){
|
||||||
|
JsonArray subArray = new JsonArray();
|
||||||
|
Address address = new Address(publicKey.getKey());
|
||||||
|
subArray.add(address.toString());
|
||||||
|
subArray.add(key_auths.get(publicKey));
|
||||||
|
keyAuthArray.add(subArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(UserAccount key : account_auths.keySet()){
|
||||||
|
JsonArray subArray = new JsonArray();
|
||||||
|
subArray.add(key.toString());
|
||||||
|
subArray.add(key_auths.get(key));
|
||||||
|
accountAuthArray.add(subArray);
|
||||||
|
}
|
||||||
|
authority.add(KEY_KEY_AUTHS, keyAuthArray);
|
||||||
|
authority.add(KEY_ACCOUNT_AUTHS, accountAuthArray);
|
||||||
|
authority.add(KEY_EXTENSIONS, extensions.toJsonObject());
|
||||||
|
return authority;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] toBytes() {
|
||||||
|
List<Byte> byteArray = new ArrayList<Byte>();
|
||||||
|
// Adding number of authorities
|
||||||
|
byteArray.add(Byte.valueOf((byte) (account_auths.size() + key_auths.size())));
|
||||||
|
|
||||||
|
// 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());
|
||||||
|
|
||||||
|
//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());
|
||||||
|
|
||||||
|
// 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());
|
||||||
|
}
|
||||||
|
return Bytes.toArray(byteArray);
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,7 +14,11 @@ public abstract class BaseOperation implements ByteSerializable, JsonSerializabl
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract byte getId();
|
public byte getId() {
|
||||||
|
return (byte) this.type.ordinal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void setFee(AssetAmount assetAmount);
|
||||||
|
|
||||||
public abstract byte[] toBytes();
|
public abstract byte[] toBytes();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
package com.luminiasoft.bitshares;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by nelson on 11/9/16.
|
|
||||||
*/
|
|
||||||
public class Extension {
|
|
||||||
//TODO: Give this class a proper implementation
|
|
||||||
}
|
|
43
src/main/java/com/luminiasoft/bitshares/Extensions.java
Normal file
43
src/main/java/com/luminiasoft/bitshares/Extensions.java
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
package com.luminiasoft.bitshares;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.luminiasoft.bitshares.interfaces.ByteSerializable;
|
||||||
|
import com.luminiasoft.bitshares.interfaces.JsonSerializable;
|
||||||
|
|
||||||
|
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<JsonSerializable> extensions;
|
||||||
|
|
||||||
|
public Extensions(){
|
||||||
|
extensions = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toJsonString() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonElement toJsonObject() {
|
||||||
|
JsonArray array = new JsonArray();
|
||||||
|
for(JsonSerializable o : extensions)
|
||||||
|
array.add(o.toJsonObject());
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] toBytes() {
|
||||||
|
return new byte[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size(){
|
||||||
|
return extensions.size();
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,17 +7,21 @@ import java.io.IOException;
|
||||||
public class Main {
|
public class Main {
|
||||||
|
|
||||||
// Brain key from Nelson's app referencing the bilthon-83 account
|
// Brain key from Nelson's app referencing the bilthon-83 account
|
||||||
public static final String BRAIN_KEY = "PUMPER ISOTOME SERE STAINER CLINGER MOONLIT CHAETA UPBRIM AEDILIC BERTHER NIT SHAP SAID SHADING JUNCOUS CHOUGH";
|
public static final String BILTHON_83_BRAIN_KEY = "PUMPER ISOTOME SERE STAINER CLINGER MOONLIT CHAETA UPBRIM AEDILIC BERTHER NIT SHAP SAID SHADING JUNCOUS CHOUGH";
|
||||||
|
|
||||||
//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 BILTHON_5_BRAIN_KEY = "UNMATE AURIGAL NAVET WAVICLE REWOVE ABBOTCY COWHERB OUTKICK STOPPER JUSSORY BEAMLET WIRY";
|
||||||
//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 BILTHON_7_BRAIN_KEY = "VENIN QUOTHA OBESELY TORIC OSMATIC SPOKEN DIACOPE CUBICA TABULA REDDING APONIA TARTAR";
|
||||||
|
|
||||||
|
//public static final String BILTHON_83_BRAIN_KEY = "TWIXT SERMO TRILLI AUDIO PARDED PLUMET BIWA REHUNG MAUDLE VALVULA OUTBURN FEWNESS ALIENER UNTRACE PRICH TROKER";
|
||||||
|
//public static final String BILTHON_83_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";
|
public static final String BIP39_KEY = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
|
||||||
|
|
||||||
// WIF from Nelson's app referencing the bilthon-83 account
|
// WIF from Nelson's app referencing the bilthon-83 account
|
||||||
// public static final String WIF = "5J96pne45qWM1WpektoeazN6k9Mt93jQ7LyueRxFfEMTiy6yxjM";
|
// public static final String WIF = "5J96pne45qWM1WpektoeazN6k9Mt93jQ7LyueRxFfEMTiy6yxjM";
|
||||||
// Brain key from an empty account created by the cli_wallet
|
// Brain key from an empty account created by the cli_wallet
|
||||||
// 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 BILTHON_83_BRAIN_KEY = "TWIXT SERMO TRILLI AUDIO PARDED PLUMET BIWA REHUNG MAUDLE VALVULA OUTBURN FEWNESS ALIENER UNTRACE PRICH TROKER";
|
||||||
// WIF from an emty account created by the cli_wallet
|
// WIF from an empty account created by the cli_wallet
|
||||||
public static final String WIF = "5KMzB2GqGhnh7ufhgddmz1eKPHS72uTLeL9hHjSvPb1UywWknF5";
|
public static final String WIF = "5KMzB2GqGhnh7ufhgddmz1eKPHS72uTLeL9hHjSvPb1UywWknF5";
|
||||||
|
|
||||||
public static final String EXTERNAL_SIGNATURE = "1f36c41acb774fcbc9c231b5895ec9701d6872729098d8ea56d78dda72a6b54252694db85d7591de5751b7aea06871da15d63a1028758421607ffc143e53ef3306";
|
public static final String EXTERNAL_SIGNATURE = "1f36c41acb774fcbc9c231b5895ec9701d6872729098d8ea56d78dda72a6b54252694db85d7591de5751b7aea06871da15d63a1028758421607ffc143e53ef3306";
|
||||||
|
@ -38,7 +42,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();
|
||||||
|
@ -46,9 +50,10 @@ public class Main {
|
||||||
// test.testGetDynamicParams();
|
// test.testGetDynamicParams();
|
||||||
// test.testGetRequiredFeesSerialization();
|
// test.testGetRequiredFeesSerialization();
|
||||||
// test.testRequiredFeesResponse();
|
// test.testRequiredFeesResponse();
|
||||||
// test.testTransactionBroadcastSequence();
|
test.testTransactionBroadcastSequence();
|
||||||
// test.testAccountLookupDeserialization();
|
// test.testAccountLookupDeserialization();
|
||||||
// test.testPrivateKeyManipulations();
|
// test.testPrivateKeyManipulations();
|
||||||
|
// test.testPublicKeyManipulations();
|
||||||
// test.testGetAccountByName();
|
// test.testGetAccountByName();
|
||||||
// test.testGetRequiredFees();
|
// test.testGetRequiredFees();
|
||||||
// test.testRandomNumberGeneration();
|
// test.testRandomNumberGeneration();
|
||||||
|
@ -59,8 +64,10 @@ public class Main {
|
||||||
// test.testRelativeAccountHistory();
|
// test.testRelativeAccountHistory();
|
||||||
// test.testingInvoiceGeneration();
|
// test.testingInvoiceGeneration();
|
||||||
// test.testCompression();
|
// test.testCompression();
|
||||||
//test.testCreateBinFile();
|
// test.testAccountUpdateSerialization();
|
||||||
//test.testImportBinFile();
|
// test.testAccountUpdateOperationBroadcast();
|
||||||
test.testLookout();
|
// test.testCreateBinFile();
|
||||||
|
// test.testImportBinFile();
|
||||||
|
//test.testLookupAccounts();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
33
src/main/java/com/luminiasoft/bitshares/Optional.java
Normal file
33
src/main/java/com/luminiasoft/bitshares/Optional.java
Normal file
|
@ -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<T extends GrapheneSerializable> 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();
|
||||||
|
}
|
||||||
|
}
|
24
src/main/java/com/luminiasoft/bitshares/PublicKey.java
Normal file
24
src/main/java/com/luminiasoft/bitshares/PublicKey.java
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ECKey getKey(){
|
||||||
|
return publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] toBytes() {
|
||||||
|
return publicKey.getPubKey();
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,5 +15,5 @@ public class RPC {
|
||||||
public static final String CALL_GET_ACCOUNTS = "get_accounts";
|
public static final String CALL_GET_ACCOUNTS = "get_accounts";
|
||||||
public static final String CALL_GET_KEY_REFERENCES = "get_key_references";
|
public static final String CALL_GET_KEY_REFERENCES = "get_key_references";
|
||||||
public static final String CALL_GET_RELATIVE_ACCOUNT_HISTORY = "get_relative_account_history";
|
public static final String CALL_GET_RELATIVE_ACCOUNT_HISTORY = "get_relative_account_history";
|
||||||
public static final String CALL_GET_ID_BY_NAME = "lookup_accounts";
|
public static final String CALL_LOOKUP_ACCOUNTS = "lookup_accounts";
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
import com.google.gson.JsonArray;
|
import com.google.gson.JsonArray;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import com.luminiasoft.bitshares.errors.MalformedAddressException;
|
||||||
import com.luminiasoft.bitshares.errors.MalformedTransactionException;
|
import com.luminiasoft.bitshares.errors.MalformedTransactionException;
|
||||||
import com.luminiasoft.bitshares.interfaces.WitnessResponseListener;
|
import com.luminiasoft.bitshares.interfaces.WitnessResponseListener;
|
||||||
import com.luminiasoft.bitshares.models.*;
|
import com.luminiasoft.bitshares.models.*;
|
||||||
|
@ -12,17 +13,12 @@ import com.luminiasoft.bitshares.test.NaiveSSLContext;
|
||||||
import com.luminiasoft.bitshares.ws.*;
|
import com.luminiasoft.bitshares.ws.*;
|
||||||
import com.neovisionaries.ws.client.*;
|
import com.neovisionaries.ws.client.*;
|
||||||
import org.bitcoinj.core.*;
|
import org.bitcoinj.core.*;
|
||||||
import org.spongycastle.crypto.Digest;
|
|
||||||
import org.spongycastle.crypto.digests.RIPEMD160Digest;
|
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.SSLContext;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.math.BigInteger;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
@ -186,144 +182,14 @@ public class Test {
|
||||||
UserAccount to = new UserAccount("1.2.129848");
|
UserAccount to = new UserAccount("1.2.129848");
|
||||||
AssetAmount amount = new AssetAmount(UnsignedLong.valueOf(100), new Asset("1.3.120"));
|
AssetAmount amount = new AssetAmount(UnsignedLong.valueOf(100), new Asset("1.3.120"));
|
||||||
AssetAmount fee = new AssetAmount(UnsignedLong.valueOf(264174), new Asset("1.3.0"));
|
AssetAmount fee = new AssetAmount(UnsignedLong.valueOf(264174), new Asset("1.3.0"));
|
||||||
operations.add(new Transfer(from, to, amount, fee));
|
operations.add(new TransferOperation(from, to, amount, fee));
|
||||||
this.transaction = new Transaction(Main.WIF, blockData, operations);
|
BrainKey brainKey = new BrainKey(Main.BILTHON_83_BRAIN_KEY, 0);
|
||||||
|
this.transaction = new Transaction(brainKey.getWalletImportFormat(), blockData, operations);
|
||||||
byte[] serializedTransaction = this.transaction.toBytes();
|
byte[] serializedTransaction = this.transaction.toBytes();
|
||||||
System.out.println("Serialized transaction");
|
System.out.println("Serialized transaction");
|
||||||
System.out.println(Util.bytesToHex(serializedTransaction));
|
System.out.println(Util.bytesToHex(serializedTransaction));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testWebSocketTransfer() throws IOException {
|
|
||||||
String login = "{\"id\":%d,\"method\":\"call\",\"params\":[1,\"login\",[\"\",\"\"]]}";
|
|
||||||
String getDatabaseId = "{\"method\": \"call\", \"params\": [1, \"database\", []], \"jsonrpc\": \"2.0\", \"id\": %d}";
|
|
||||||
String getHistoryId = "{\"method\": \"call\", \"params\": [1, \"history\", []], \"jsonrpc\": \"2.0\", \"id\": %d}";
|
|
||||||
String getNetworkBroadcastId = "{\"method\": \"call\", \"params\": [1, \"network_broadcast\", []], \"jsonrpc\": \"2.0\", \"id\": %d}";
|
|
||||||
String getDynamicParameters = "{\"method\": \"call\", \"params\": [0, \"get_dynamic_global_properties\", []], \"jsonrpc\": \"2.0\", \"id\": %d}";
|
|
||||||
String rawPayload = "{\"method\": \"call\", \"params\": [%d, \"broadcast_transaction\", [{\"expiration\": \"%s\", \"signatures\": [\"%s\"], \"operations\": [[0, {\"fee\": {\"amount\": 264174, \"asset_id\": \"1.3.0\"}, \"amount\": {\"amount\": 100, \"asset_id\": \"1.3.120\"}, \"to\": \"1.2.129848\", \"extensions\": [], \"from\": \"1.2.138632\"}]], \"ref_block_num\": %d, \"extensions\": [], \"ref_block_prefix\": %d}]], \"jsonrpc\": \"2.0\", \"id\": %d}";
|
|
||||||
|
|
||||||
// String url = "wss://bitshares.openledger.info/ws";
|
|
||||||
String url = "ws://api.devling.xyz:8088";
|
|
||||||
WebSocketFactory factory = new WebSocketFactory().setConnectionTimeout(5000);
|
|
||||||
|
|
||||||
// Create a WebSocket. The timeout value set above is used.
|
|
||||||
WebSocket ws = factory.createSocket(url);
|
|
||||||
|
|
||||||
ws.addListener(new WebSocketAdapter() {
|
|
||||||
|
|
||||||
private DynamicGlobalProperties dynProperties;
|
|
||||||
private int networkBroadcastApiId;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onConnected(WebSocket websocket, Map<String, List<String>> headers) throws Exception {
|
|
||||||
System.out.println("onConnected");
|
|
||||||
String payload = String.format(login, 1);
|
|
||||||
System.out.println(">>");
|
|
||||||
System.out.println(payload);
|
|
||||||
websocket.sendText(payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDisconnected(WebSocket websocket, WebSocketFrame serverCloseFrame, WebSocketFrame clientCloseFrame, boolean closedByServer) throws Exception {
|
|
||||||
System.out.println("onDisconnected");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTextFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
|
|
||||||
System.out.println("<<");
|
|
||||||
String response = frame.getPayloadText();
|
|
||||||
System.out.println(response);
|
|
||||||
Gson gson = new Gson();
|
|
||||||
BaseResponse baseResponse = gson.fromJson(response, BaseResponse.class);
|
|
||||||
// if(baseResponse.id.equals("1")){
|
|
||||||
// String payload = String.format(getDatabaseId, 2);
|
|
||||||
// System.out.println(">>");
|
|
||||||
// System.out.println(payload);
|
|
||||||
// websocket.sendText(payload);
|
|
||||||
// }else if(baseResponse.id.equals("2")){
|
|
||||||
// String payload = String.format(getHistoryId, 3);
|
|
||||||
// System.out.println(">>");
|
|
||||||
// System.out.println(payload);
|
|
||||||
// websocket.sendText(payload);
|
|
||||||
// }else if(baseResponse.id.equals("3")){
|
|
||||||
if (baseResponse.id == 1) {
|
|
||||||
String payload = String.format(getNetworkBroadcastId, 2);
|
|
||||||
System.out.println(">>");
|
|
||||||
System.out.println(payload);
|
|
||||||
websocket.sendText(payload);
|
|
||||||
// }else if(baseResponse.id.equals("4")){
|
|
||||||
}
|
|
||||||
if (baseResponse.id == 2) {
|
|
||||||
String payload = String.format(getDynamicParameters, 3);
|
|
||||||
Type ApiIdResponse = new TypeToken<WitnessResponse<Integer>>() {
|
|
||||||
}.getType();
|
|
||||||
WitnessResponse<Integer> witnessResponse = gson.fromJson(response, ApiIdResponse);
|
|
||||||
networkBroadcastApiId = witnessResponse.result.intValue();
|
|
||||||
System.out.println(">>");
|
|
||||||
System.out.println(payload);
|
|
||||||
websocket.sendText(payload);
|
|
||||||
} else if (baseResponse.id == 3) {
|
|
||||||
// Got dynamic properties
|
|
||||||
Type DynamicGlobalPropertiesResponse = new TypeToken<WitnessResponse<DynamicGlobalProperties>>() {
|
|
||||||
}.getType();
|
|
||||||
WitnessResponse<DynamicGlobalProperties> witnessResponse = gson.fromJson(response, DynamicGlobalPropertiesResponse);
|
|
||||||
dynProperties = witnessResponse.result;
|
|
||||||
|
|
||||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
|
||||||
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
|
|
||||||
Date date = dateFormat.parse(dynProperties.time);
|
|
||||||
long expirationTime = (date.getTime() / 1000) + 30;
|
|
||||||
testTransactionSerialization(dynProperties.head_block_number, dynProperties.head_block_id, expirationTime);
|
|
||||||
|
|
||||||
BlockData blockData = new BlockData(dynProperties.head_block_number, dynProperties.head_block_id, expirationTime);
|
|
||||||
byte[] signatureBytes = signMessage();
|
|
||||||
|
|
||||||
String payload = String.format(
|
|
||||||
rawPayload,
|
|
||||||
networkBroadcastApiId,
|
|
||||||
dateFormat.format(new Date(expirationTime * 1000)),
|
|
||||||
Util.bytesToHex(signatureBytes),
|
|
||||||
blockData.getRefBlockNum(),
|
|
||||||
blockData.getRefBlockPrefix(),
|
|
||||||
4);
|
|
||||||
System.out.println(">>");
|
|
||||||
System.out.println(payload);
|
|
||||||
websocket.sendText(payload);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(WebSocket websocket, WebSocketException cause) throws Exception {
|
|
||||||
System.out.println("onError");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onUnexpectedError(WebSocket websocket, WebSocketException cause) throws Exception {
|
|
||||||
System.out.println("onUnexpectedError");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleCallbackError(WebSocket websocket, Throwable cause) throws Exception {
|
|
||||||
System.out.println("handleCallbackError. Msg: " + cause.getMessage());
|
|
||||||
StackTraceElement[] stackTrace = cause.getStackTrace();
|
|
||||||
for (StackTraceElement line : stackTrace) {
|
|
||||||
System.out.println(line.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
try {
|
|
||||||
// Connect to the server and perform an opening handshake.
|
|
||||||
// This method blocks until the opening handshake is finished.
|
|
||||||
ws.connect();
|
|
||||||
} catch (OpeningHandshakeException e) {
|
|
||||||
// A violation against the WebSocket protocol was detected
|
|
||||||
// during the opening handshake.
|
|
||||||
System.out.println("OpeningHandshakeException");
|
|
||||||
} catch (WebSocketException e) {
|
|
||||||
// Failed to establish a WebSocket connection.
|
|
||||||
System.out.println("WebSocketException. Msg: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testCustomSerializer() {
|
public void testCustomSerializer() {
|
||||||
AssetAmount amount = new AssetAmount(UnsignedLong.valueOf(100), new Asset("1.3.120"));
|
AssetAmount amount = new AssetAmount(UnsignedLong.valueOf(100), new Asset("1.3.120"));
|
||||||
String jsonAmount = amount.toJsonString();
|
String jsonAmount = amount.toJsonString();
|
||||||
|
@ -343,8 +209,8 @@ public class Test {
|
||||||
.setDestination(new UserAccount("1.2.129848"))
|
.setDestination(new UserAccount("1.2.129848"))
|
||||||
.setAmount(new AssetAmount(UnsignedLong.valueOf(100), new Asset("1.3.120")))
|
.setAmount(new AssetAmount(UnsignedLong.valueOf(100), new Asset("1.3.120")))
|
||||||
.setFee(new AssetAmount(UnsignedLong.valueOf(264174), new Asset("1.3.0")))
|
.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())
|
.setPrivateKey(DumpedPrivateKey.fromBase58(null, Main.WIF).getKey())
|
||||||
|
.setBlockData(new BlockData(Main.REF_BLOCK_NUM, Main.REF_BLOCK_PREFIX, Main.RELATIVE_EXPIRATION))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
ArrayList<Serializable> transactionList = new ArrayList<>();
|
ArrayList<Serializable> transactionList = new ArrayList<>();
|
||||||
|
@ -429,14 +295,12 @@ public class Test {
|
||||||
.setAmount(new AssetAmount(UnsignedLong.valueOf(100), new Asset("1.3.120")))
|
.setAmount(new AssetAmount(UnsignedLong.valueOf(100), new Asset("1.3.120")))
|
||||||
.setFee(new AssetAmount(UnsignedLong.valueOf(264174), new Asset("1.3.0")))
|
.setFee(new AssetAmount(UnsignedLong.valueOf(264174), new Asset("1.3.0")))
|
||||||
.setBlockData(new BlockData(43408, 1430521623, 1479231969))
|
.setBlockData(new BlockData(43408, 1430521623, 1479231969))
|
||||||
.setPrivateKey(DumpedPrivateKey.fromBase58(null, Main.WIF).getKey())
|
.setPrivateKey(DumpedPrivateKey.fromBase58(null, Main.BILTHON_5_BRAIN_KEY).getKey())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
ArrayList<Serializable> transactionList = new ArrayList<>();
|
ArrayList<Serializable> transactionList = new ArrayList<>();
|
||||||
transactionList.add(transaction);
|
transactionList.add(transaction);
|
||||||
|
|
||||||
transactionList.add(transaction);
|
|
||||||
|
|
||||||
SSLContext context = null;
|
SSLContext context = null;
|
||||||
context = NaiveSSLContext.getInstance("TLS");
|
context = NaiveSSLContext.getInstance("TLS");
|
||||||
WebSocketFactory factory = new WebSocketFactory();
|
WebSocketFactory factory = new WebSocketFactory();
|
||||||
|
@ -446,7 +310,7 @@ public class Test {
|
||||||
|
|
||||||
WebSocket mWebSocket = factory.createSocket(OPENLEDGER_WITNESS_URL);
|
WebSocket mWebSocket = factory.createSocket(OPENLEDGER_WITNESS_URL);
|
||||||
|
|
||||||
mWebSocket.addListener(new TransactionBroadcastSequence(transaction, listener));
|
mWebSocket.addListener(new TransactionBroadcastSequence(transaction, new Asset("1.3.0"), listener));
|
||||||
mWebSocket.connect();
|
mWebSocket.connect();
|
||||||
|
|
||||||
} catch (MalformedTransactionException e) {
|
} catch (MalformedTransactionException e) {
|
||||||
|
@ -472,14 +336,29 @@ public class Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPrivateKeyManipulations() {
|
public void testPrivateKeyManipulations() {
|
||||||
ECKey privateKey = DumpedPrivateKey.fromBase58(null, Main.WIF).getKey();
|
String brainKeyWords = "PUMPER ISOTOME SERE STAINER CLINGER MOONLIT CHAETA UPBRIM AEDILIC BERTHER NIT SHAP SAID SHADING JUNCOUS CHOUGH";
|
||||||
|
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("private key..............: " + Util.bytesToHex(privateKey.getSecretBytes()));
|
||||||
System.out.println("public key uncompressed..: " + Util.bytesToHex(privateKey.getPubKey()));
|
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("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.getPubKeyPoint().getEncoded(true)));
|
||||||
System.out.println("base58...................: " + Base58.encode(privateKey.getPubKey()));
|
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() {
|
public void testGetAccountByName() {
|
||||||
|
@ -502,7 +381,7 @@ public class Test {
|
||||||
UserAccount to = new UserAccount("1.2.129848");
|
UserAccount to = new UserAccount("1.2.129848");
|
||||||
AssetAmount amount = new AssetAmount(UnsignedLong.valueOf(100), new Asset("1.3.120"));
|
AssetAmount amount = new AssetAmount(UnsignedLong.valueOf(100), new Asset("1.3.120"));
|
||||||
AssetAmount fee = new AssetAmount(UnsignedLong.valueOf(264174), new Asset("1.3.0"));
|
AssetAmount fee = new AssetAmount(UnsignedLong.valueOf(264174), new Asset("1.3.0"));
|
||||||
Transfer transfer = new Transfer(from, to, amount, fee);
|
TransferOperation transfer = new TransferOperation(from, to, amount, fee);
|
||||||
ArrayList<BaseOperation> operations = new ArrayList<>();
|
ArrayList<BaseOperation> operations = new ArrayList<>();
|
||||||
operations.add(transfer);
|
operations.add(transfer);
|
||||||
|
|
||||||
|
@ -521,52 +400,9 @@ 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
|
* The final purpose of this test is to convert the plain brainkey at
|
||||||
* Main.BRAIN_KEY into the WIF at Main.WIF
|
* Main.BILTHON_83_BRAIN_KEY into the WIF at Main.WIF
|
||||||
*/
|
*/
|
||||||
public void testBrainKeyOperations(boolean random) {
|
public void testBrainKeyOperations(boolean random) {
|
||||||
try {
|
try {
|
||||||
|
@ -581,32 +417,27 @@ public class Test {
|
||||||
String suggestion = BrainKey.suggest(words);
|
String suggestion = BrainKey.suggest(words);
|
||||||
brainKey = new BrainKey(suggestion, 0);
|
brainKey = new BrainKey(suggestion, 0);
|
||||||
} else {
|
} else {
|
||||||
brainKey = new BrainKey(Main.BRAIN_KEY, 0);
|
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();
|
ECKey key = brainKey.getPrivateKey();
|
||||||
System.out.println("Private key");
|
System.out.println("Private key..................: "+Util.bytesToHex(key.getSecretBytes()));
|
||||||
System.out.println(Util.bytesToHex(key.getSecretBytes()));
|
|
||||||
String wif = key.getPrivateKeyAsWiF(NetworkParameters.fromID(NetworkParameters.ID_MAINNET));
|
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));
|
String wif2 = key.decompress().getPrivateKeyAsWiF(NetworkParameters.fromID(NetworkParameters.ID_MAINNET));
|
||||||
System.out.println("wif decompressed: " + wif2);
|
System.out.println("Wif Decompressed.............: " + wif2);
|
||||||
|
System.out.println("Wif from BrainKey............: "+ brainKey.getWalletImportFormat());
|
||||||
|
|
||||||
byte[] pubKey1 = key.decompress().getPubKey();
|
byte[] uncompressedPubKey = key.decompress().getPubKey();
|
||||||
System.out.println("decompressed public key: " + Base58.encode(pubKey1));
|
byte[] compressedPubKey = key.getPubKey();
|
||||||
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("Public Key Decompressed......: " + Util.bytesToHex(uncompressedPubKey));
|
||||||
System.out.println("pub key uncompressed : " + Util.bytesToHex(pubKey2));
|
System.out.println("Public Key Compressed........: " + Util.bytesToHex(compressedPubKey));
|
||||||
|
|
||||||
byte[] pubKey3 = key.getPubKeyPoint().getEncoded(true);
|
|
||||||
System.out.println("pub key compressed : " + Base58.encode(pubKey3));
|
|
||||||
|
|
||||||
// Address generation test
|
// Address generation test
|
||||||
Address address = new Address(key);
|
Address address = new Address(ECKey.fromPublicOnly(key.getPubKey()));
|
||||||
System.out.println("Block explorer's address: " + address);
|
System.out.println("Block explorer's address.....: " + address);
|
||||||
|
|
||||||
System.out.println("Wif: : " + brainKey.getWalletImportFormat());
|
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
System.out.println("FileNotFoundException. Msg: " + e.getMessage());
|
System.out.println("FileNotFoundException. Msg: " + e.getMessage());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -641,7 +472,7 @@ public class Test {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
BrainKey brainKey = new BrainKey(Main.BRAIN_KEY, 0);
|
BrainKey brainKey = new BrainKey(Main.BILTHON_83_BRAIN_KEY, 0);
|
||||||
Address address = new Address(brainKey.getPrivateKey());
|
Address address = new Address(brainKey.getPrivateKey());
|
||||||
try {
|
try {
|
||||||
// Create a custom SSL context.
|
// Create a custom SSL context.
|
||||||
|
@ -729,7 +560,7 @@ public class Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testCreateBinFile() {
|
public void testCreateBinFile() {
|
||||||
byte[] fileOutput = FileBin.getBytesFromBrainKey(Main.BRAIN_KEY, "123456", "bithon-83");
|
byte[] fileOutput = FileBin.getBytesFromBrainKey(Main.BILTHON_83_BRAIN_KEY, "123456", "bithon-83");
|
||||||
///String stringFile = "02f9f3eb0f61a0a96134975d86048bf92e114d6a1ce286140cad3a96c33e697282bc0a8a24d1ad0c7bc084a79816ce38e36bd2d624aa8bf686f53fb4c7e25e3974da9b40e0b17e9d0b5b82793a04b19646169c49c58cd67f4950aee7d275141dd24f52baaaee772995a9bd6a6562a7a38aae08951236d3f612aecef7aedd720a91eacbab3a792ca3ebe0105838fe11f6e9d0e83e5d77eb82f17c7ba85c670e69294a8bcf8365cfeca487a60093498496bbec394c729e3fda9f32fdccdea56288b36fb14a26aa309b548a6dd9c1d616d22167348f8d580f9dc7361b4457d2dc6d75ec985d8e2d3dcdff89cd425d9f14037ac961eb10ac5f92bab356ccecd8cf018ec05ab40d915b628a75ae32cfa4005634f08b24c0dc8c5a7636ed70cbd86a7f0c4f6236d74310470fafe3af8b5346c8cb61957f7292b468d276498f9e806399588b0afd5777e6ee5fe7cd3a6691d9b5486cb5c7adbd5ad0b17588dd32d82b01d49ecf0f2bf24ee54a490ee620e8ab049047ffa416b5efa8f1f0155d8f1be866a10d0d62ae44a3a8ecc0121c08837c2ee1a25f8b6dd7266273c41f4b9a5e3d600e3fb4de870f99ab1a7196d93f222595f92e97a2480f58b61b62639154a374b987664fd317622aaad156f831b03f2d9606537b65b3b1fcfb1fb6be39560ad2c301dd1fc25cee755e61b49ebfe42ca7e64b4b0fc4aa347b48a85c0b585a3499fe278e25cb2141f8009b9afc875fa2a2c439bf6cdec4b5190a6deb7f9390f072beb24749a8a2114cc1870c07be079abb3ee0ebc827f9b53e158a529bc6552eba280f05edf5f7ae1911de7acb4888150a509d029ec7c9da6de8adabbca6773a0a293a0a42de8278c82e88b9390b42b56f58bd8633fb97130e799a47a744e2e8958fd5";
|
///String stringFile = "02f9f3eb0f61a0a96134975d86048bf92e114d6a1ce286140cad3a96c33e697282bc0a8a24d1ad0c7bc084a79816ce38e36bd2d624aa8bf686f53fb4c7e25e3974da9b40e0b17e9d0b5b82793a04b19646169c49c58cd67f4950aee7d275141dd24f52baaaee772995a9bd6a6562a7a38aae08951236d3f612aecef7aedd720a91eacbab3a792ca3ebe0105838fe11f6e9d0e83e5d77eb82f17c7ba85c670e69294a8bcf8365cfeca487a60093498496bbec394c729e3fda9f32fdccdea56288b36fb14a26aa309b548a6dd9c1d616d22167348f8d580f9dc7361b4457d2dc6d75ec985d8e2d3dcdff89cd425d9f14037ac961eb10ac5f92bab356ccecd8cf018ec05ab40d915b628a75ae32cfa4005634f08b24c0dc8c5a7636ed70cbd86a7f0c4f6236d74310470fafe3af8b5346c8cb61957f7292b468d276498f9e806399588b0afd5777e6ee5fe7cd3a6691d9b5486cb5c7adbd5ad0b17588dd32d82b01d49ecf0f2bf24ee54a490ee620e8ab049047ffa416b5efa8f1f0155d8f1be866a10d0d62ae44a3a8ecc0121c08837c2ee1a25f8b6dd7266273c41f4b9a5e3d600e3fb4de870f99ab1a7196d93f222595f92e97a2480f58b61b62639154a374b987664fd317622aaad156f831b03f2d9606537b65b3b1fcfb1fb6be39560ad2c301dd1fc25cee755e61b49ebfe42ca7e64b4b0fc4aa347b48a85c0b585a3499fe278e25cb2141f8009b9afc875fa2a2c439bf6cdec4b5190a6deb7f9390f072beb24749a8a2114cc1870c07be079abb3ee0ebc827f9b53e158a529bc6552eba280f05edf5f7ae1911de7acb4888150a509d029ec7c9da6de8adabbca6773a0a293a0a42de8278c82e88b9390b42b56f58bd8633fb97130e799a47a744e2e8958fd5";
|
||||||
//fileOutput = new BigInteger(stringFile, 16).toByteArray();
|
//fileOutput = new BigInteger(stringFile, 16).toByteArray();
|
||||||
System.out.println(FileBin.getBrainkeyFromByte(fileOutput, "123456"));
|
System.out.println(FileBin.getBrainkeyFromByte(fileOutput, "123456"));
|
||||||
|
@ -742,9 +573,63 @@ public class Test {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void testLookout() {
|
public void testAccountUpdateSerialization() {
|
||||||
|
String newAddress = "BTS8RiFgs8HkcVPVobHLKEv6yL3iXcC9SWjbPVS15dDAXLG9GYhnY";
|
||||||
try {
|
try {
|
||||||
GetAccountIdByName query = new GetAccountIdByName("henrutest-125", mListener);
|
Address address = new Address(newAddress);
|
||||||
|
HashMap<PublicKey, Integer> 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAccountUpdateOperationBroadcast(){
|
||||||
|
|
||||||
|
WitnessResponseListener listener = new WitnessResponseListener() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(WitnessResponse response) {
|
||||||
|
System.out.println("onSuccess");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(BaseResponse.Error error) {
|
||||||
|
System.out.println("onError");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
String newAddress = "BTS8RiFgs8HkcVPVobHLKEv6yL3iXcC9SWjbPVS15dDAXLG9GYhnY";
|
||||||
|
try {
|
||||||
|
Address address = new Address(newAddress);
|
||||||
|
HashMap<PublicKey, Integer> 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;
|
SSLContext context = null;
|
||||||
context = NaiveSSLContext.getInstance("TLS");
|
context = NaiveSSLContext.getInstance("TLS");
|
||||||
WebSocketFactory factory = new WebSocketFactory();
|
WebSocketFactory factory = new WebSocketFactory();
|
||||||
|
@ -753,14 +638,55 @@ public class Test {
|
||||||
factory.setSSLContext(context);
|
factory.setSSLContext(context);
|
||||||
|
|
||||||
WebSocket mWebSocket = factory.createSocket(OPENLEDGER_WITNESS_URL);
|
WebSocket mWebSocket = factory.createSocket(OPENLEDGER_WITNESS_URL);
|
||||||
mWebSocket.addListener(query);
|
|
||||||
|
mWebSocket.addListener(new TransactionBroadcastSequence(transaction, new Asset("1.3.0"), listener));
|
||||||
mWebSocket.connect();
|
mWebSocket.connect();
|
||||||
} catch (WebSocketException ex) {
|
|
||||||
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
|
} catch (MalformedAddressException e) {
|
||||||
} catch (IOException ex) {
|
System.out.println("MalformedAddressException. Msg: "+e.getMessage());
|
||||||
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
|
} catch (MalformedTransactionException e) {
|
||||||
} catch (NoSuchAlgorithmException ex) {
|
System.out.println("MalformedTransactionException. Msg: "+e.getMessage());
|
||||||
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
|
} 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testLookupAccounts(){
|
||||||
|
WitnessResponseListener listener = new WitnessResponseListener() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(WitnessResponse response) {
|
||||||
|
System.out.println("onSuccess");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(BaseResponse.Error error) {
|
||||||
|
System.out.println("onError");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SSLContext context = null;
|
||||||
|
try {
|
||||||
|
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 LookupAccounts("bilthon", listener));
|
||||||
|
mWebSocket.connect();
|
||||||
|
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
System.out.println("NoSuchAlgorithmException. Msg: "+e.getMessage());
|
||||||
|
} catch (WebSocketException e) {
|
||||||
|
System.out.println("WebSocketException. Msg: "+e.getMessage());
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.out.println("IOException. Msg: "+e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gson.JsonSerializationContext;
|
import com.google.gson.JsonSerializationContext;
|
||||||
import com.google.gson.JsonSerializer;
|
import com.google.gson.JsonSerializer;
|
||||||
|
import com.luminiasoft.bitshares.errors.MalformedTransactionException;
|
||||||
import com.luminiasoft.bitshares.interfaces.ByteSerializable;
|
import com.luminiasoft.bitshares.interfaces.ByteSerializable;
|
||||||
import com.luminiasoft.bitshares.interfaces.JsonSerializable;
|
import com.luminiasoft.bitshares.interfaces.JsonSerializable;
|
||||||
|
|
||||||
|
@ -23,11 +24,12 @@ import java.util.List;
|
||||||
import java.util.TimeZone;
|
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 {
|
public class Transaction implements ByteSerializable, JsonSerializable {
|
||||||
private final String TAG = this.getClass().getName();
|
private final String TAG = this.getClass().getName();
|
||||||
|
|
||||||
|
public static final int DEFAULT_EXPIRATION_TIME = 30;
|
||||||
public static final String KEY_EXPIRATION = "expiration";
|
public static final String KEY_EXPIRATION = "expiration";
|
||||||
public static final String KEY_SIGNATURES = "signatures";
|
public static final String KEY_SIGNATURES = "signatures";
|
||||||
public static final String KEY_OPERATIONS = "operations";
|
public static final String KEY_OPERATIONS = "operations";
|
||||||
|
@ -38,20 +40,7 @@ public class Transaction implements ByteSerializable, JsonSerializable {
|
||||||
private ECKey privateKey;
|
private ECKey privateKey;
|
||||||
private BlockData blockData;
|
private BlockData blockData;
|
||||||
private List<BaseOperation> operations;
|
private List<BaseOperation> operations;
|
||||||
private List<Extension> extensions;
|
private List<Extensions> extensions;
|
||||||
|
|
||||||
/**
|
|
||||||
* Transaction 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<BaseOperation> operation_list){
|
|
||||||
this.privateKey = DumpedPrivateKey.fromBase58(null, wif).getKey();
|
|
||||||
this.blockData = block_data;
|
|
||||||
this.operations = operation_list;
|
|
||||||
this.extensions = new ArrayList<Extension>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transaction constructor.
|
* Transaction constructor.
|
||||||
|
@ -63,7 +52,34 @@ public class Transaction implements ByteSerializable, JsonSerializable {
|
||||||
this.privateKey = privateKey;
|
this.privateKey = privateKey;
|
||||||
this.blockData = blockData;
|
this.blockData = blockData;
|
||||||
this.operations = operationList;
|
this.operations = operationList;
|
||||||
this.extensions = new ArrayList<Extension>();
|
this.extensions = new ArrayList<Extensions>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transaction 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<BaseOperation> operation_list){
|
||||||
|
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<AssetAmount> fees){
|
||||||
|
for(int i = 0; i < operations.size(); i++)
|
||||||
|
operations.get(i).setFee(fees.get(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ECKey getPrivateKey(){
|
public ECKey getPrivateKey(){
|
||||||
|
@ -117,6 +133,7 @@ public class Transaction implements ByteSerializable, JsonSerializable {
|
||||||
}
|
}
|
||||||
return sigData;
|
return sigData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method that creates a serialized byte array with compact information about this transaction
|
* Method that creates a serialized byte array with compact information about this transaction
|
||||||
* that is needed for the creation of a signature.
|
* that is needed for the creation of a signature.
|
||||||
|
@ -142,8 +159,8 @@ public class Transaction implements ByteSerializable, JsonSerializable {
|
||||||
//Adding the number of extensions
|
//Adding the number of extensions
|
||||||
byteArray.add((byte) this.extensions.size());
|
byteArray.add((byte) this.extensions.size());
|
||||||
|
|
||||||
for(Extension extension : extensions){
|
for(Extensions extensions : this.extensions){
|
||||||
//TODO: Implement the extension serialization
|
//TODO: Implement the extensions serialization
|
||||||
}
|
}
|
||||||
// Adding a last zero byte to match the result obtained by the python-graphenelib code
|
// Adding a last zero byte to match the result obtained by the python-graphenelib code
|
||||||
// I'm not exactly sure what's the meaning of this last zero byte, but for now I'll just
|
// I'm not exactly sure what's the meaning of this last zero byte, but for now I'll just
|
||||||
|
|
|
@ -11,5 +11,16 @@ public abstract class TransactionBuilder {
|
||||||
protected ECKey privateKey;
|
protected ECKey privateKey;
|
||||||
protected BlockData blockData;
|
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;
|
public abstract Transaction build() throws MalformedTransactionException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
package com.luminiasoft.bitshares;
|
package com.luminiasoft.bitshares;
|
||||||
|
|
||||||
import com.google.common.primitives.Bytes;
|
import com.google.common.primitives.Bytes;
|
||||||
import com.google.common.primitives.UnsignedLong;
|
|
||||||
import com.google.gson.*;
|
import com.google.gson.*;
|
||||||
|
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class used to encapsulate the Transfer operation related functionalities.
|
* Class used to encapsulate the TransferOperation operation related functionalities.
|
||||||
* TODO: Add extensions support
|
* TODO: Add extensions support
|
||||||
*/
|
*/
|
||||||
public class Transfer extends BaseOperation {
|
public class TransferOperation extends BaseOperation {
|
||||||
public static final String KEY_FEE = "fee";
|
public static final String KEY_FEE = "fee";
|
||||||
public static final String KEY_AMOUNT = "amount";
|
public static final String KEY_AMOUNT = "amount";
|
||||||
public static final String KEY_EXTENSIONS = "extensions";
|
public static final String KEY_EXTENSIONS = "extensions";
|
||||||
|
@ -24,7 +23,7 @@ public class Transfer extends BaseOperation {
|
||||||
private Memo memo;
|
private Memo memo;
|
||||||
private String[] extensions;
|
private String[] extensions;
|
||||||
|
|
||||||
public Transfer(UserAccount from, UserAccount to, AssetAmount transferAmount, AssetAmount fee){
|
public TransferOperation(UserAccount from, UserAccount to, AssetAmount transferAmount, AssetAmount fee){
|
||||||
super(OperationType.transfer_operation);
|
super(OperationType.transfer_operation);
|
||||||
this.from = from;
|
this.from = from;
|
||||||
this.to = to;
|
this.to = to;
|
||||||
|
@ -33,7 +32,7 @@ public class Transfer extends BaseOperation {
|
||||||
this.memo = new Memo();
|
this.memo = new Memo();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Transfer(UserAccount from, UserAccount to, AssetAmount transferAmount){
|
public TransferOperation(UserAccount from, UserAccount to, AssetAmount transferAmount){
|
||||||
super(OperationType.transfer_operation);
|
super(OperationType.transfer_operation);
|
||||||
this.from = from;
|
this.from = from;
|
||||||
this.to = to;
|
this.to = to;
|
||||||
|
@ -41,15 +40,11 @@ public class Transfer extends BaseOperation {
|
||||||
this.memo = new Memo();
|
this.memo = new Memo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void setFee(AssetAmount newFee){
|
public void setFee(AssetAmount newFee){
|
||||||
this.fee = newFee;
|
this.fee = newFee;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte getId() {
|
|
||||||
return (byte) this.type.ordinal();
|
|
||||||
}
|
|
||||||
|
|
||||||
public UserAccount getFrom(){
|
public UserAccount getFrom(){
|
||||||
return this.from;
|
return this.from;
|
||||||
}
|
}
|
||||||
|
@ -78,8 +73,9 @@ public class Transfer extends BaseOperation {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toJsonString() {
|
public String toJsonString() {
|
||||||
|
//TODO: Evaluate using simple Gson class to return a simple string representation and drop the TransferSerializer class
|
||||||
GsonBuilder gsonBuilder = new GsonBuilder();
|
GsonBuilder gsonBuilder = new GsonBuilder();
|
||||||
gsonBuilder.registerTypeAdapter(Transfer.class, new TransferSerializer());
|
gsonBuilder.registerTypeAdapter(TransferOperation.class, new TransferSerializer());
|
||||||
return gsonBuilder.create().toJson(this);
|
return gsonBuilder.create().toJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,10 +93,10 @@ public class Transfer extends BaseOperation {
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class TransferSerializer implements JsonSerializer<Transfer> {
|
public static class TransferSerializer implements JsonSerializer<TransferOperation> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JsonElement serialize(Transfer transfer, Type type, JsonSerializationContext jsonSerializationContext) {
|
public JsonElement serialize(TransferOperation transfer, Type type, JsonSerializationContext jsonSerializationContext) {
|
||||||
JsonArray arrayRep = new JsonArray();
|
JsonArray arrayRep = new JsonArray();
|
||||||
arrayRep.add(transfer.getId());
|
arrayRep.add(transfer.getId());
|
||||||
arrayRep.add(transfer.toJsonObject());
|
arrayRep.add(transfer.toJsonObject());
|
||||||
|
@ -131,12 +127,12 @@ public class Transfer extends BaseOperation {
|
||||||
* }
|
* }
|
||||||
* ]
|
* ]
|
||||||
*
|
*
|
||||||
* It will convert this data into a nice Transfer object.
|
* It will convert this data into a nice TransferOperation object.
|
||||||
*/
|
*/
|
||||||
public static class TransferDeserializer implements JsonDeserializer<Transfer> {
|
public static class TransferDeserializer implements JsonDeserializer<TransferOperation> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Transfer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
public TransferOperation deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||||
if(json.isJsonArray()){
|
if(json.isJsonArray()){
|
||||||
// This block is used just to check if we are in the first step of the deserialization
|
// This block is used just to check if we are in the first step of the deserialization
|
||||||
// when we are dealing with an array.
|
// when we are dealing with an array.
|
||||||
|
@ -146,7 +142,7 @@ public class Transfer extends BaseOperation {
|
||||||
return null;
|
return null;
|
||||||
}else{
|
}else{
|
||||||
// Calling itself recursively, this is only done once, so there will be no problems.
|
// Calling itself recursively, this is only done once, so there will be no problems.
|
||||||
return context.deserialize(serializedTransfer.get(1), Transfer.class);
|
return context.deserialize(serializedTransfer.get(1), TransferOperation.class);
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
// This block is called in the second recursion and takes care of deserializing the
|
// This block is called in the second recursion and takes care of deserializing the
|
||||||
|
@ -160,7 +156,7 @@ public class Transfer extends BaseOperation {
|
||||||
// Deserializing UserAccount objects
|
// Deserializing UserAccount objects
|
||||||
UserAccount from = new UserAccount(jsonObject.get("from").getAsString());
|
UserAccount from = new UserAccount(jsonObject.get("from").getAsString());
|
||||||
UserAccount to = new UserAccount(jsonObject.get("to").getAsString());
|
UserAccount to = new UserAccount(jsonObject.get("to").getAsString());
|
||||||
Transfer transfer = new Transfer(from, to, amount, fee);
|
TransferOperation transfer = new TransferOperation(from, to, amount, fee);
|
||||||
return transfer;
|
return transfer;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -16,6 +16,12 @@ public class TransferTransactionBuilder extends TransactionBuilder {
|
||||||
private AssetAmount transferAmount;
|
private AssetAmount transferAmount;
|
||||||
private AssetAmount feeAmount;
|
private AssetAmount feeAmount;
|
||||||
|
|
||||||
|
public TransferTransactionBuilder(){}
|
||||||
|
|
||||||
|
public TransferTransactionBuilder(ECKey privKey) {
|
||||||
|
super(privKey);
|
||||||
|
}
|
||||||
|
|
||||||
public TransferTransactionBuilder setPrivateKey(ECKey key){
|
public TransferTransactionBuilder setPrivateKey(ECKey key){
|
||||||
this.privateKey = key;
|
this.privateKey = key;
|
||||||
return this;
|
return this;
|
||||||
|
@ -46,7 +52,8 @@ public class TransferTransactionBuilder extends TransactionBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TransferTransactionBuilder addOperation(Transfer transferOperation){
|
//TODO: Add support for multiple transfer operations in a single transaction
|
||||||
|
public TransferTransactionBuilder addOperation(TransferOperation transferOperation){
|
||||||
if(operations == null){
|
if(operations == null){
|
||||||
operations = new ArrayList<BaseOperation>();
|
operations = new ArrayList<BaseOperation>();
|
||||||
}
|
}
|
||||||
|
@ -57,8 +64,6 @@ public class TransferTransactionBuilder extends TransactionBuilder {
|
||||||
public Transaction build() throws MalformedTransactionException {
|
public Transaction build() throws MalformedTransactionException {
|
||||||
if(privateKey == null){
|
if(privateKey == null){
|
||||||
throw new MalformedTransactionException("Missing private key information");
|
throw new MalformedTransactionException("Missing private key information");
|
||||||
}else if(blockData == null){
|
|
||||||
throw new MalformedTransactionException("Missing block data information");
|
|
||||||
}else if(operations == null){
|
}else if(operations == null){
|
||||||
// If the operations list has not been set, we might be able to build one with the
|
// If the operations list has not been set, we might be able to build one with the
|
||||||
// previously provided data. But in order for this to work we have to have all
|
// previously provided data. But in order for this to work we have to have all
|
||||||
|
@ -73,11 +78,11 @@ public class TransferTransactionBuilder extends TransactionBuilder {
|
||||||
if(transferAmount == null){
|
if(transferAmount == null){
|
||||||
throw new MalformedTransactionException("Missing transfer amount information");
|
throw new MalformedTransactionException("Missing transfer amount information");
|
||||||
}
|
}
|
||||||
Transfer transferOperation;
|
TransferOperation transferOperation;
|
||||||
if(feeAmount == null){
|
if(feeAmount == null){
|
||||||
transferOperation = new Transfer(sourceAccount, destinationAccount, transferAmount);
|
transferOperation = new TransferOperation(sourceAccount, destinationAccount, transferAmount);
|
||||||
}else{
|
}else{
|
||||||
transferOperation = new Transfer(sourceAccount, destinationAccount, transferAmount, feeAmount);
|
transferOperation = new TransferOperation(sourceAccount, destinationAccount, transferAmount, feeAmount);
|
||||||
}
|
}
|
||||||
operations.add(transferOperation);
|
operations.add(transferOperation);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package com.luminiasoft.bitshares;
|
package com.luminiasoft.bitshares;
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.*;
|
||||||
import com.luminiasoft.bitshares.interfaces.ByteSerializable;
|
import com.luminiasoft.bitshares.interfaces.ByteSerializable;
|
||||||
import com.luminiasoft.bitshares.interfaces.JsonSerializable;
|
import com.luminiasoft.bitshares.interfaces.JsonSerializable;
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import java.io.ByteArrayOutputStream;
|
||||||
import java.io.DataOutput;
|
import java.io.DataOutput;
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class tha represents a graphene user account.
|
* Class tha represents a graphene user account.
|
||||||
|
@ -15,15 +16,37 @@ import java.io.IOException;
|
||||||
*/
|
*/
|
||||||
public class UserAccount extends GrapheneObject implements ByteSerializable, JsonSerializable {
|
public class UserAccount extends GrapheneObject implements ByteSerializable, JsonSerializable {
|
||||||
|
|
||||||
|
public static final String PROXY_TO_SELF = "1.2.5";
|
||||||
|
|
||||||
|
private String accountName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor that expects a user account in the string representation.
|
* Constructor that expects a user account in the string representation.
|
||||||
* That is in the 1.2.x format.
|
* That is in the 1.2.x format.
|
||||||
* @param id: The string representing the account apiId.
|
* @param id: The string representing the user account.
|
||||||
*/
|
*/
|
||||||
public UserAccount(String id) {
|
public UserAccount(String id) {
|
||||||
super(id);
|
super(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor that expects a user account withe the proper graphene object id and an account name.
|
||||||
|
* @param id: The string representing the user account.
|
||||||
|
* @param name: The name of this user account.
|
||||||
|
*/
|
||||||
|
public UserAccount(String id, String name){
|
||||||
|
super(id);
|
||||||
|
this.accountName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getter for the account name field.
|
||||||
|
* @return: The name of this account.
|
||||||
|
*/
|
||||||
|
public String getAccountName() {
|
||||||
|
return accountName;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] toBytes() {
|
public byte[] toBytes() {
|
||||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||||
|
@ -45,4 +68,29 @@ public class UserAccount extends GrapheneObject implements ByteSerializable, Jso
|
||||||
public JsonObject toJsonObject() {
|
public JsonObject toJsonObject() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.toJsonString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom deserializer used to deserialize user accounts provided as response from the 'lookup_accounts' api call.
|
||||||
|
* This response contains serialized user accounts in the form [[{id1},{name1}][{id1},{name1}]].
|
||||||
|
*
|
||||||
|
* For instance:
|
||||||
|
* [["bilthon-1","1.2.139205"],["bilthon-2","1.2.139207"],["bilthon-2016","1.2.139262"]]
|
||||||
|
*
|
||||||
|
* So this class will pick up this data and turn it into a UserAccount object.
|
||||||
|
*/
|
||||||
|
public static class UserAccountDeserializer implements JsonDeserializer<UserAccount> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserAccount deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||||
|
JsonArray array = json.getAsJsonArray();
|
||||||
|
String name = array.get(0).getAsString();
|
||||||
|
String id = array.get(1).getAsString();
|
||||||
|
return new UserAccount(id, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.luminiasoft.bitshares;
|
package com.luminiasoft.bitshares;
|
||||||
|
|
||||||
import org.tukaani.xz.FinishableOutputStream;
|
import org.tukaani.xz.FinishableOutputStream;
|
||||||
|
import com.google.common.primitives.Bytes;
|
||||||
import org.tukaani.xz.LZMA2Options;
|
import org.tukaani.xz.LZMA2Options;
|
||||||
import org.tukaani.xz.LZMAInputStream;
|
import org.tukaani.xz.LZMAInputStream;
|
||||||
import org.tukaani.xz.LZMAOutputStream;
|
import org.tukaani.xz.LZMAOutputStream;
|
||||||
|
@ -11,6 +12,7 @@ import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
@ -114,4 +116,33 @@ public class Util {
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of bytes with the underlying data used to represent an integer in the reverse form.
|
||||||
|
* This is useful for endianess switches, meaning that if you give this function a big-endian integer
|
||||||
|
* it will return it's little-endian bytes.
|
||||||
|
* @param input An Integer value.
|
||||||
|
* @return The array of bytes that represent this value in the reverse format.
|
||||||
|
*/
|
||||||
|
public static byte[] revertInteger(Integer input){
|
||||||
|
return ByteBuffer.allocate(Integer.SIZE / 8).putInt(Integer.reverseBytes(input)).array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same operation as in the revertInteger function, but in this case for a short (2 bytes) value.
|
||||||
|
* @param input A Short value
|
||||||
|
* @return The array of bytes that represent this value in the reverse format.
|
||||||
|
*/
|
||||||
|
public static byte[] revertShort(Short input){
|
||||||
|
return ByteBuffer.allocate(Short.SIZE / 8).putShort(Short.reverseBytes(input)).array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same operation as in the revertInteger function, but in this case for a long (8 bytes) value.
|
||||||
|
* @param input A Long value
|
||||||
|
* @return The array of bytes that represent this value in the reverse format.
|
||||||
|
*/
|
||||||
|
public static byte[] revertLong(Long input){
|
||||||
|
return ByteBuffer.allocate(Long.SIZE / 8).putLong(Long.reverseBytes(input)).array();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
33
src/main/java/com/luminiasoft/bitshares/Vote.java
Normal file
33
src/main/java/com/luminiasoft/bitshares/Vote.java
Normal file
|
@ -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 };
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.luminiasoft.bitshares.interfaces;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface used to group both ByteSerializable and JsonSerializable interfaces.
|
||||||
|
*/
|
||||||
|
public interface GrapheneSerializable extends ByteSerializable, JsonSerializable {
|
||||||
|
}
|
|
@ -5,7 +5,8 @@ import com.google.gson.JsonElement;
|
||||||
import java.io.Serializable;
|
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 {
|
public interface JsonSerializable extends Serializable {
|
||||||
|
|
||||||
|
|
|
@ -88,6 +88,8 @@ public class ApiCall implements JsonSerializable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
methodParams.add(array);
|
methodParams.add(array);
|
||||||
|
}else{
|
||||||
|
System.out.println("Skipping parameter of type: "+this.params.get(i).getClass());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
paramsArray.add(methodParams);
|
paramsArray.add(methodParams);
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package com.luminiasoft.bitshares.models;
|
package com.luminiasoft.bitshares.models;
|
||||||
|
|
||||||
import com.luminiasoft.bitshares.BaseOperation;
|
import com.luminiasoft.bitshares.TransferOperation;
|
||||||
import com.luminiasoft.bitshares.GrapheneObject;
|
|
||||||
import com.luminiasoft.bitshares.Transfer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class offers support to deserialization of transfer operations received by the API
|
* This class offers support to deserialization of transfer operations received by the API
|
||||||
|
@ -13,7 +11,7 @@ import com.luminiasoft.bitshares.Transfer;
|
||||||
*/
|
*/
|
||||||
public class HistoricalTransfer {
|
public class HistoricalTransfer {
|
||||||
public String id;
|
public String id;
|
||||||
public Transfer op;
|
public TransferOperation op;
|
||||||
public Object[] result;
|
public Object[] result;
|
||||||
public long block_num;
|
public long block_num;
|
||||||
public long trx_in_block;
|
public long trx_in_block;
|
||||||
|
|
|
@ -5,7 +5,7 @@ import com.google.gson.GsonBuilder;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
import com.luminiasoft.bitshares.AssetAmount;
|
import com.luminiasoft.bitshares.AssetAmount;
|
||||||
import com.luminiasoft.bitshares.RPC;
|
import com.luminiasoft.bitshares.RPC;
|
||||||
import com.luminiasoft.bitshares.Transfer;
|
import com.luminiasoft.bitshares.TransferOperation;
|
||||||
import com.luminiasoft.bitshares.UserAccount;
|
import com.luminiasoft.bitshares.UserAccount;
|
||||||
import com.luminiasoft.bitshares.interfaces.WitnessResponseListener;
|
import com.luminiasoft.bitshares.interfaces.WitnessResponseListener;
|
||||||
import com.luminiasoft.bitshares.models.ApiCall;
|
import com.luminiasoft.bitshares.models.ApiCall;
|
||||||
|
@ -119,7 +119,7 @@ public class GetRelativeAccountHistory extends WebSocketAdapter {
|
||||||
System.out.println(frame.getPayloadText());
|
System.out.println(frame.getPayloadText());
|
||||||
Type RelativeAccountHistoryResponse = new TypeToken<WitnessResponse<List<HistoricalTransfer>>>(){}.getType();
|
Type RelativeAccountHistoryResponse = new TypeToken<WitnessResponse<List<HistoricalTransfer>>>(){}.getType();
|
||||||
GsonBuilder gsonBuilder = new GsonBuilder();
|
GsonBuilder gsonBuilder = new GsonBuilder();
|
||||||
gsonBuilder.registerTypeAdapter(Transfer.class, new Transfer.TransferDeserializer());
|
gsonBuilder.registerTypeAdapter(TransferOperation.class, new TransferOperation.TransferDeserializer());
|
||||||
gsonBuilder.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetDeserializer());
|
gsonBuilder.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetDeserializer());
|
||||||
WitnessResponse<List<HistoricalTransfer>> transfersResponse = gsonBuilder.create().fromJson(response, RelativeAccountHistoryResponse);
|
WitnessResponse<List<HistoricalTransfer>> transfersResponse = gsonBuilder.create().fromJson(response, RelativeAccountHistoryResponse);
|
||||||
mListener.onSuccess(transfersResponse);
|
mListener.onSuccess(transfersResponse);
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
package com.luminiasoft.bitshares.ws;
|
package com.luminiasoft.bitshares.ws;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.GsonBuilder;
|
||||||
import com.google.gson.JsonArray;
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
import com.luminiasoft.bitshares.RPC;
|
import com.luminiasoft.bitshares.RPC;
|
||||||
|
import com.luminiasoft.bitshares.UserAccount;
|
||||||
import com.luminiasoft.bitshares.interfaces.WitnessResponseListener;
|
import com.luminiasoft.bitshares.interfaces.WitnessResponseListener;
|
||||||
import com.luminiasoft.bitshares.models.ApiCall;
|
import com.luminiasoft.bitshares.models.ApiCall;
|
||||||
import com.luminiasoft.bitshares.models.BaseResponse;
|
import com.luminiasoft.bitshares.models.BaseResponse;
|
||||||
|
@ -22,13 +22,22 @@ import java.util.Map;
|
||||||
/**
|
/**
|
||||||
* Created by henry on 07/12/16.
|
* Created by henry on 07/12/16.
|
||||||
*/
|
*/
|
||||||
public class GetAccountIdByName extends WebSocketAdapter {
|
public class LookupAccounts extends WebSocketAdapter {
|
||||||
|
|
||||||
|
public static final int DEFAULT_MAX = 1000;
|
||||||
private final String accountName;
|
private final String accountName;
|
||||||
|
private int maxAccounts = DEFAULT_MAX;
|
||||||
private final WitnessResponseListener mListener;
|
private final WitnessResponseListener mListener;
|
||||||
|
|
||||||
public GetAccountIdByName(String accountName, WitnessResponseListener listener){
|
public LookupAccounts(String accountName, WitnessResponseListener listener){
|
||||||
this.accountName = accountName;
|
this.accountName = accountName;
|
||||||
|
this.maxAccounts = DEFAULT_MAX;
|
||||||
|
this.mListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LookupAccounts(String accountName, int maxAccounts, WitnessResponseListener listener){
|
||||||
|
this.accountName = accountName;
|
||||||
|
this.maxAccounts = maxAccounts;
|
||||||
this.mListener = listener;
|
this.mListener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,19 +45,20 @@ public class GetAccountIdByName extends WebSocketAdapter {
|
||||||
public void onConnected(WebSocket websocket, Map<String, List<String>> headers) throws Exception {
|
public void onConnected(WebSocket websocket, Map<String, List<String>> headers) throws Exception {
|
||||||
ArrayList<Serializable> accountParams = new ArrayList<>();
|
ArrayList<Serializable> accountParams = new ArrayList<>();
|
||||||
accountParams.add(this.accountName);
|
accountParams.add(this.accountName);
|
||||||
accountParams.add(50);
|
accountParams.add(this.maxAccounts);
|
||||||
ApiCall getAccountByName = new ApiCall(0, RPC.CALL_GET_ID_BY_NAME, accountParams, "2.0", 1);
|
ApiCall getAccountByName = new ApiCall(0, RPC.CALL_LOOKUP_ACCOUNTS, accountParams, RPC.VERSION, 1);
|
||||||
websocket.sendText(getAccountByName.toJsonString());
|
websocket.sendText(getAccountByName.toJsonString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTextFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
|
public void onTextFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
|
||||||
|
System.out.println("<<< "+frame.getPayloadText());
|
||||||
String response = frame.getPayloadText();
|
String response = frame.getPayloadText();
|
||||||
Gson gson = new Gson();
|
|
||||||
|
|
||||||
Type GetAccountByNameResponse = new TypeToken<WitnessResponse<JsonArray>>(){}.getType();
|
|
||||||
WitnessResponse<WitnessResponse<JsonArray>> witnessResponse = gson.fromJson(response, GetAccountByNameResponse);
|
|
||||||
|
|
||||||
|
Type LookupAccountsResponse = new TypeToken<WitnessResponse<List<UserAccount>>>(){}.getType();
|
||||||
|
GsonBuilder builder = new GsonBuilder();
|
||||||
|
builder.registerTypeAdapter(UserAccount.class, new UserAccount.UserAccountDeserializer());
|
||||||
|
WitnessResponse<List<UserAccount>> witnessResponse = builder.create().fromJson(response, LookupAccountsResponse);
|
||||||
if(witnessResponse.error != null){
|
if(witnessResponse.error != null){
|
||||||
this.mListener.onError(witnessResponse.error);
|
this.mListener.onError(witnessResponse.error);
|
||||||
}else{
|
}else{
|
||||||
|
@ -58,6 +68,12 @@ public class GetAccountIdByName extends WebSocketAdapter {
|
||||||
websocket.disconnect();
|
websocket.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFrameSent(WebSocket websocket, WebSocketFrame frame) throws Exception {
|
||||||
|
if(frame.isTextFrame())
|
||||||
|
System.out.println(">>> "+frame.getPayloadText());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(WebSocket websocket, WebSocketException cause) throws Exception {
|
public void onError(WebSocket websocket, WebSocketException cause) throws Exception {
|
||||||
mListener.onError(new BaseResponse.Error(cause.getMessage()));
|
mListener.onError(new BaseResponse.Error(cause.getMessage()));
|
|
@ -1,13 +1,9 @@
|
||||||
package com.luminiasoft.bitshares.ws;
|
package com.luminiasoft.bitshares.ws;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
import com.luminiasoft.bitshares.BaseOperation;
|
import com.luminiasoft.bitshares.*;
|
||||||
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.interfaces.WitnessResponseListener;
|
import com.luminiasoft.bitshares.interfaces.WitnessResponseListener;
|
||||||
import com.luminiasoft.bitshares.models.ApiCall;
|
import com.luminiasoft.bitshares.models.ApiCall;
|
||||||
import com.luminiasoft.bitshares.models.BaseResponse;
|
import com.luminiasoft.bitshares.models.BaseResponse;
|
||||||
|
@ -21,11 +17,7 @@ import com.neovisionaries.ws.client.WebSocketFrame;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.TimeZone;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class that will handle the transaction publication procedure.
|
* Class that will handle the transaction publication procedure.
|
||||||
|
@ -36,18 +28,15 @@ public class TransactionBroadcastSequence extends WebSocketAdapter {
|
||||||
private final static int LOGIN_ID = 1;
|
private final static int LOGIN_ID = 1;
|
||||||
private final static int GET_NETWORK_BROADCAST_ID = 2;
|
private final static int GET_NETWORK_BROADCAST_ID = 2;
|
||||||
private final static int GET_NETWORK_DYNAMIC_PARAMETERS = 3;
|
private final static int GET_NETWORK_DYNAMIC_PARAMETERS = 3;
|
||||||
private final static int BROADCAST_TRANSACTION = 4;
|
private final static int GET_REQUIRED_FEES = 4;
|
||||||
public final static int EXPIRATION_TIME = 30;
|
private final static int BROADCAST_TRANSACTION = 5;
|
||||||
|
|
||||||
|
private Asset feeAsset;
|
||||||
private Transaction transaction;
|
private Transaction transaction;
|
||||||
private long expirationTime;
|
|
||||||
private String headBlockId;
|
|
||||||
private long headBlockNumber;
|
|
||||||
private WitnessResponseListener mListener;
|
private WitnessResponseListener mListener;
|
||||||
|
|
||||||
private int currentId = 1;
|
private int currentId = 1;
|
||||||
private int broadcastApiId = -1;
|
private int broadcastApiId = -1;
|
||||||
private int retries = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor of this class. The ids required
|
* Constructor of this class. The ids required
|
||||||
|
@ -56,8 +45,9 @@ public class TransactionBroadcastSequence extends WebSocketAdapter {
|
||||||
* be implemented by the party interested in being notified about the success/failure
|
* be implemented by the party interested in being notified about the success/failure
|
||||||
* of the transaction broadcast operation.
|
* of the transaction broadcast operation.
|
||||||
*/
|
*/
|
||||||
public TransactionBroadcastSequence(Transaction transaction, WitnessResponseListener listener){
|
public TransactionBroadcastSequence(Transaction transaction, Asset feeAsset, WitnessResponseListener listener){
|
||||||
this.transaction = transaction;
|
this.transaction = transaction;
|
||||||
|
this.feeAsset = feeAsset;
|
||||||
this.mListener = listener;
|
this.mListener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,30 +56,39 @@ public class TransactionBroadcastSequence extends WebSocketAdapter {
|
||||||
ArrayList<Serializable> loginParams = new ArrayList<>();
|
ArrayList<Serializable> loginParams = new ArrayList<>();
|
||||||
loginParams.add(null);
|
loginParams.add(null);
|
||||||
loginParams.add(null);
|
loginParams.add(null);
|
||||||
ApiCall loginCall = new ApiCall(1, RPC.CALL_LOGIN, loginParams, "2.0", currentId);
|
ApiCall loginCall = new ApiCall(1, RPC.CALL_LOGIN, loginParams, RPC.VERSION, currentId);
|
||||||
websocket.sendText(loginCall.toJsonString());
|
websocket.sendText(loginCall.toJsonString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTextFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
|
public void onTextFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
|
||||||
|
if(frame.isTextFrame())
|
||||||
|
System.out.println("<<< "+frame.getPayloadText());
|
||||||
String response = frame.getPayloadText();
|
String response = frame.getPayloadText();
|
||||||
Gson gson = new Gson();
|
Gson gson = new Gson();
|
||||||
BaseResponse baseResponse = gson.fromJson(response, BaseResponse.class);
|
BaseResponse baseResponse = gson.fromJson(response, BaseResponse.class);
|
||||||
if(baseResponse.error != null && baseResponse.error.message.indexOf("is_canonical") == -1){
|
if(baseResponse.error != null){
|
||||||
mListener.onError(baseResponse.error);
|
mListener.onError(baseResponse.error);
|
||||||
websocket.disconnect();
|
websocket.disconnect();
|
||||||
}else{
|
}else{
|
||||||
currentId++;
|
currentId++;
|
||||||
ArrayList<Serializable> emptyParams = new ArrayList<>();
|
ArrayList<Serializable> emptyParams = new ArrayList<>();
|
||||||
if(baseResponse.id == LOGIN_ID){
|
if(baseResponse.id == LOGIN_ID){
|
||||||
ApiCall networkApiIdCall = new ApiCall(1, RPC.CALL_NETWORK_BROADCAST, emptyParams, "2.0", currentId);
|
ApiCall networkApiIdCall = new ApiCall(1, RPC.CALL_NETWORK_BROADCAST, emptyParams, RPC.VERSION, currentId);
|
||||||
websocket.sendText(networkApiIdCall.toJsonString());
|
websocket.sendText(networkApiIdCall.toJsonString());
|
||||||
}else if(baseResponse.id == GET_NETWORK_BROADCAST_ID){
|
}else if(baseResponse.id == GET_NETWORK_BROADCAST_ID){
|
||||||
Type ApiIdResponse = new TypeToken<WitnessResponse<Integer>>() {}.getType();
|
Type ApiIdResponse = new TypeToken<WitnessResponse<Integer>>() {}.getType();
|
||||||
WitnessResponse<Integer> witnessResponse = gson.fromJson(response, ApiIdResponse);
|
WitnessResponse<Integer> witnessResponse = gson.fromJson(response, ApiIdResponse);
|
||||||
broadcastApiId = witnessResponse.result;
|
broadcastApiId = witnessResponse.result;
|
||||||
|
|
||||||
ApiCall getDynamicParametersCall = new ApiCall(0, RPC.CALL_GET_DYNAMIC_GLOBAL_PROPERTIES, emptyParams, "2.0", currentId);
|
// Building API call to request dynamic network properties
|
||||||
|
ApiCall getDynamicParametersCall = new ApiCall(0,
|
||||||
|
RPC.CALL_GET_DYNAMIC_GLOBAL_PROPERTIES,
|
||||||
|
emptyParams,
|
||||||
|
RPC.VERSION,
|
||||||
|
currentId);
|
||||||
|
|
||||||
|
// Requesting network properties
|
||||||
websocket.sendText(getDynamicParametersCall.toJsonString());
|
websocket.sendText(getDynamicParametersCall.toJsonString());
|
||||||
}else if(baseResponse.id == GET_NETWORK_DYNAMIC_PARAMETERS){
|
}else if(baseResponse.id == GET_NETWORK_DYNAMIC_PARAMETERS){
|
||||||
Type DynamicGlobalPropertiesResponse = new TypeToken<WitnessResponse<DynamicGlobalProperties>>(){}.getType();
|
Type DynamicGlobalPropertiesResponse = new TypeToken<WitnessResponse<DynamicGlobalProperties>>(){}.getType();
|
||||||
|
@ -100,76 +99,68 @@ public class TransactionBroadcastSequence extends WebSocketAdapter {
|
||||||
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
|
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||||
Date date = dateFormat.parse(dynamicProperties.time);
|
Date date = dateFormat.parse(dynamicProperties.time);
|
||||||
|
|
||||||
// Obtained block data
|
// Adjusting dynamic block data to every transaction
|
||||||
expirationTime = (date.getTime() / 1000) + EXPIRATION_TIME;
|
long expirationTime = (date.getTime() / 1000) + Transaction.DEFAULT_EXPIRATION_TIME;
|
||||||
headBlockId = dynamicProperties.head_block_id;
|
String headBlockId = dynamicProperties.head_block_id;
|
||||||
headBlockNumber = dynamicProperties.head_block_number;
|
long headBlockNumber = dynamicProperties.head_block_number;
|
||||||
|
transaction.setBlockData(new BlockData(headBlockNumber, headBlockId, expirationTime));
|
||||||
|
|
||||||
|
// Building a new API call to request fees information
|
||||||
|
ArrayList<Serializable> accountParams = new ArrayList<>();
|
||||||
|
accountParams.add((Serializable) transaction.getOperations());
|
||||||
|
accountParams.add(this.feeAsset.getObjectId());
|
||||||
|
ApiCall getRequiredFees = new ApiCall(0, RPC.CALL_GET_REQUIRED_FEES, accountParams, RPC.VERSION, currentId);
|
||||||
|
|
||||||
|
// Requesting fee amount
|
||||||
|
websocket.sendText(getRequiredFees.toJsonString());
|
||||||
|
}else if(baseResponse.id == GET_REQUIRED_FEES){
|
||||||
|
Type GetRequiredFeesResponse = new TypeToken<WitnessResponse<List<AssetAmount>>>(){}.getType();
|
||||||
|
GsonBuilder gsonBuilder = new GsonBuilder();
|
||||||
|
gsonBuilder.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetDeserializer());
|
||||||
|
WitnessResponse<List<AssetAmount>> requiredFeesResponse = gsonBuilder.create().fromJson(response, GetRequiredFeesResponse);
|
||||||
|
|
||||||
|
// Setting fees
|
||||||
|
transaction.setFees(requiredFeesResponse.result);
|
||||||
|
ArrayList<Serializable> transactions = new ArrayList<>();
|
||||||
|
transactions.add(transaction);
|
||||||
|
|
||||||
ArrayList<Serializable> transactionList = new ArrayList<>();
|
|
||||||
transactionList.add(transaction);
|
|
||||||
ApiCall call = new ApiCall(broadcastApiId,
|
ApiCall call = new ApiCall(broadcastApiId,
|
||||||
RPC.CALL_BROADCAST_TRANSACTION,
|
RPC.CALL_BROADCAST_TRANSACTION,
|
||||||
transactionList,
|
transactions,
|
||||||
"2.0",
|
RPC.VERSION,
|
||||||
currentId);
|
currentId);
|
||||||
|
|
||||||
// Finally sending transaction
|
// Finally broadcasting transaction
|
||||||
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<WitnessResponse<String>> witnessResponse = gson.fromJson(response, WitnessResponseType);
|
||||||
if(witnessResponse.error == null){
|
|
||||||
mListener.onSuccess(witnessResponse);
|
mListener.onSuccess(witnessResponse);
|
||||||
websocket.disconnect();
|
websocket.disconnect();
|
||||||
}else{
|
|
||||||
if(witnessResponse.error.message.indexOf("is_canonical") != -1 && retries < 10){
|
|
||||||
/*
|
|
||||||
* This is a very ugly hack, but it will do for now.
|
|
||||||
*
|
|
||||||
* The issue is that the witness is complaining about the signature not
|
|
||||||
* being canonical even though the bitcoinj ECKey.ECDSASignature.isCanonical()
|
|
||||||
* method says it is! We'll have to dive deeper into this issue and avoid
|
|
||||||
* this error altogether
|
|
||||||
*
|
|
||||||
* But this MUST BE FIXED! Since this hack will only work for transactions
|
|
||||||
* with ONE transfer operation.
|
|
||||||
*/
|
|
||||||
retries++;
|
|
||||||
List<BaseOperation> operations = this.transaction.getOperations();
|
|
||||||
Transfer transfer = (Transfer) operations.get(0);
|
|
||||||
transaction = 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())
|
|
||||||
.build();
|
|
||||||
ArrayList<Serializable> transactionList = new ArrayList<>();
|
|
||||||
transactionList.add(transaction);
|
|
||||||
ApiCall call = new ApiCall(broadcastApiId,
|
|
||||||
RPC.CALL_BROADCAST_TRANSACTION,
|
|
||||||
transactionList,
|
|
||||||
"2.0",
|
|
||||||
currentId);
|
|
||||||
websocket.sendText(call.toJsonString());
|
|
||||||
}else{
|
|
||||||
mListener.onError(witnessResponse.error);
|
|
||||||
websocket.disconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFrameSent(WebSocket websocket, WebSocketFrame frame) throws Exception {
|
||||||
|
if(frame.isTextFrame()){
|
||||||
|
System.out.println(">>> "+frame.getPayloadText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(WebSocket websocket, WebSocketException cause) throws Exception {
|
public void onError(WebSocket websocket, WebSocketException cause) throws Exception {
|
||||||
|
System.out.println("onError. cause: "+cause.getMessage());
|
||||||
mListener.onError(new BaseResponse.Error(cause.getMessage()));
|
mListener.onError(new BaseResponse.Error(cause.getMessage()));
|
||||||
websocket.disconnect();
|
websocket.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleCallbackError(WebSocket websocket, Throwable cause) throws Exception {
|
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()));
|
mListener.onError(new BaseResponse.Error(cause.getMessage()));
|
||||||
websocket.disconnect();
|
websocket.disconnect();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue