# Conflicts:
#	app/src/main/java/cy/agorise/crystalwallet/activities/ImportSeedActivity.java
#	app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/ImportSeedValidator.java
This commit is contained in:
Javier Varona 2017-10-03 23:47:08 -04:00
commit 25ecf59bf2
108 changed files with 144 additions and 9663 deletions

View file

@ -55,5 +55,6 @@ dependencies {
compile 'org.tukaani:xz:1.6' compile 'org.tukaani:xz:1.6'
compile 'com.jakewharton:butterknife:8.8.1' compile 'com.jakewharton:butterknife:8.8.1'
apt 'com.jakewharton:butterknife-compiler:8.8.1' apt 'com.jakewharton:butterknife-compiler:8.8.1'
compile 'com.github.bilthon:graphenej:0.4.5'
} }

View file

@ -36,7 +36,7 @@ public class TransactionListTest {
@Before @Before
public void addingTransactions(){ public void addingTransactions(){
db = CrystalDatabase.getAppDatabase(InstrumentationRegistry.getTargetContext()); db = CrystalDatabase.getAppDatabase(InstrumentationRegistry.getTargetContext());
transactions = RandomTransactionsGenerator.generateTransactions(numberOfTransactions,1262304001,1496275201,1,999999999); //transactions = RandomTransactionsGenerator.generateTransactions(numberOfTransactions,1262304001,1496275201,1,999999999);
for(int i=0;i<transactions.size();i++) { for(int i=0;i<transactions.size();i++) {
db.transactionDao().insertTransaction(transactions.get(i)); db.transactionDao().insertTransaction(transactions.get(i));

View file

@ -21,6 +21,8 @@
</activity> </activity>
<activity android:name=".activities.ImportSeedActivity" > <activity android:name=".activities.ImportSeedActivity" >
</activity> </activity>
<service android:name=".service.CrystalWalletService"
android:exported="false"/>
</application> </application>
</manifest> </manifest>

View file

@ -43,4 +43,20 @@ public class ValidateImportBitsharesAccountRequest extends CryptoNetInfoRequest
this._fireOnCarryOutEvent(); this._fireOnCarryOutEvent();
} }
} }
public String getAccountName() {
return accountName;
}
public void setAccountName(String accountName) {
this.accountName = accountName;
}
public String getMnemonic() {
return mnemonic;
}
public void setMnemonic(String mnemonic) {
this.mnemonic = mnemonic;
}
} }

View file

@ -1,5 +1,8 @@
package cy.agorise.crystalwallet.manager; package cy.agorise.crystalwallet.manager;
import cy.agorise.crystalwallet.apigenerator.ApiRequest;
import cy.agorise.crystalwallet.apigenerator.ApiRequestListener;
import cy.agorise.crystalwallet.apigenerator.GrapheneApiGenerator;
import cy.agorise.crystalwallet.cryptonetinforequests.CryptoNetInfoRequest; import cy.agorise.crystalwallet.cryptonetinforequests.CryptoNetInfoRequest;
import cy.agorise.crystalwallet.cryptonetinforequests.CryptoNetInfoRequestsListener; import cy.agorise.crystalwallet.cryptonetinforequests.CryptoNetInfoRequestsListener;
import cy.agorise.crystalwallet.cryptonetinforequests.ValidateImportBitsharesAccountRequest; import cy.agorise.crystalwallet.cryptonetinforequests.ValidateImportBitsharesAccountRequest;
@ -28,6 +31,31 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
@Override @Override
public void onNewRequest(CryptoNetInfoRequest request) { public void onNewRequest(CryptoNetInfoRequest request) {
if (request instanceof ValidateImportBitsharesAccountRequest){ if (request instanceof ValidateImportBitsharesAccountRequest){
final ValidateImportBitsharesAccountRequest importRequest = (ValidateImportBitsharesAccountRequest) request;
ApiRequest checkAccountName = new ApiRequest(0, new ApiRequestListener() {
@Override
public void success(Object answer, int idPetition) {
ApiRequest getAccountInfo = new ApiRequest(1,new ApiRequestListener(){
@Override
public void success(Object answer, int idPetition) {
//TODO compare keys
}
@Override
public void fail(int idPetition) {
importRequest._fireOnCarryOutEvent();
}
});
GrapheneApiGenerator.getAccountById((String)answer,getAccountInfo);
}
@Override
public void fail(int idPetition) {
importRequest._fireOnCarryOutEvent();
}
});
GrapheneApiGenerator.getAccountIdByName(importRequest.getAccountName(),checkAccountName);
} }
} }
} }

View file

@ -0,0 +1,90 @@
package cy.agorise.crystalwallet.service;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import cy.agorise.crystalwallet.manager.BitsharesAccountManager;
/**
* Created by Henry Varona on 3/10/2017.
*/
public class CrystalWalletService extends Service {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
private BitsharesAccountManager bitsharesAccountManager;
private Thread LoadAccountTransactionsThread;
private boolean keepLoadingAccountTransactions;
// Handler that receives messages from the thread
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
stopSelf(msg.arg1);
}
}
public void loadAccountTransactions(){
this.keepLoadingAccountTransactions = true;
while(this.keepLoadingAccountTransactions){
try{
Log.i("CrystalServiceLATThread","Searching for transactions...");
Thread.sleep(60000);//Sleep for 1 minutes //TODO Configurable time
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
@Override
public void onCreate() {
this.bitsharesAccountManager = new BitsharesAccountManager();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (LoadAccountTransactionsThread == null) {
LoadAccountTransactionsThread = new Thread() {
public void run() {
loadAccountTransactions();
}
};
LoadAccountTransactionsThread.start();
}
// If we get killed, after returning from here, restart
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
Log.i("Crystal Service", "Destroying service");
}
}

View file

@ -21,11 +21,13 @@ public class AccountSeedViewModel extends AndroidViewModel {
private LiveData<AccountSeed> accountSeed; private LiveData<AccountSeed> accountSeed;
private CrystalDatabase db; private CrystalDatabase db;
private MutableLiveData<ImportSeedValidator> importSeedValidator; private ImportSeedValidator importSeedValidator;
private Application app;
public AccountSeedViewModel(Application application) { public AccountSeedViewModel(Application application) {
super(application); super(application);
this.db = CrystalDatabase.getAppDatabase(application.getApplicationContext()); this.db = CrystalDatabase.getAppDatabase(application.getApplicationContext());
this.app = application;
} }
public void loadSeed(int seedId){ public void loadSeed(int seedId){
@ -34,9 +36,10 @@ public class AccountSeedViewModel extends AndroidViewModel {
public ImportSeedValidator getValidator(){ public ImportSeedValidator getValidator(){
if (this.importSeedValidator == null){ if (this.importSeedValidator == null){
this.importSeedValidator = new ImportSeedValidator(); this.importSeedValidator = new ImportSeedValidator(this.app.getResources());
} }
return null;
} }
public void addSeed(AccountSeed seed){ public void addSeed(AccountSeed seed){

View file

@ -25,9 +25,8 @@ public class ImportSeedValidator {
private boolean isValid = false; private boolean isValid = false;
public ImportSeedValidator(Resources res, AccountSeed seed){ public ImportSeedValidator(Resources res){
this.res = res; this.res = res;
this.accountSeed = seed;
this.validationFields = new ArrayList<ValidationField>(); this.validationFields = new ArrayList<ValidationField>();
//this.validationFields.add(new ValidationField("pin")); //this.validationFields.add(new ValidationField("pin"));
//this.validationFields.add(new ValidationField("pinConfirmation")); //this.validationFields.add(new ValidationField("pinConfirmation"));

View file

@ -1,166 +0,0 @@
package cy.agorise.graphenej;
import com.google.common.primitives.Bytes;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import cy.agorise.graphenej.errors.MalformedAddressException;
import cy.agorise.graphenej.interfaces.GrapheneSerializable;
/**
* 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;
}
//TODO: Implement constructor that takes a Vote array.
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()));
}
// Account options's extensions
byteArray.addAll(Bytes.asList(extensions.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;
}
/**
* Custom deserializer used while parsing the 'get_account_by_name' API call response.
* TODO: Implement all other details besides the key
*/
public static class AccountOptionsDeserializer implements JsonDeserializer<AccountOptions> {
@Override
public AccountOptions deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject baseObject = json.getAsJsonObject();
AccountOptions options;
try {
Address address = new Address(baseObject.get(KEY_MEMO_KEY).getAsString());
options = new AccountOptions(address.getPublicKey());
} catch (MalformedAddressException e) {
System.out.println("MalformedAddressException. Msg: "+e.getMessage());
options = new AccountOptions();
}
return options;
}
}
}

View file

@ -1,64 +0,0 @@
package cy.agorise.graphenej;
import com.google.common.primitives.Bytes;
import cy.agorise.graphenej.errors.MalformedAddressException;
import org.bitcoinj.core.Base58;
import org.bitcoinj.core.ECKey;
import org.spongycastle.crypto.digests.RIPEMD160Digest;
import java.util.Arrays;
/**
* Class used to encapsulate address-related operations.
*/
public class Address {
public final static String BITSHARES_PREFIX = "BTS";
private PublicKey publicKey;
private String prefix;
public Address(ECKey key) {
this.publicKey = new PublicKey(key);
this.prefix = BITSHARES_PREFIX;
}
public Address(ECKey key, String prefix) {
this.publicKey = new PublicKey(key);
this.prefix = prefix;
}
public Address(String address) throws MalformedAddressException {
this.prefix = address.substring(0, 3);
byte[] decoded = Base58.decode(address.substring(3, address.length()));
byte[] pubKey = Arrays.copyOfRange(decoded, 0, decoded.length - 4);
byte[] checksum = Arrays.copyOfRange(decoded, decoded.length - 4, decoded.length);
publicKey = new PublicKey(ECKey.fromPublicOnly(pubKey));
byte[] calculatedChecksum = calculateChecksum(pubKey);
for(int i = 0; i < calculatedChecksum.length; i++){
if(checksum[i] != calculatedChecksum[i]){
throw new MalformedAddressException("Checksum error");
}
}
}
public PublicKey getPublicKey(){
return this.publicKey;
}
@Override
public String toString() {
byte[] pubKey = this.publicKey.toBytes();
byte[] checksum = calculateChecksum(pubKey);
byte[] pubKeyChecksummed = Bytes.concat(pubKey, checksum);
return this.prefix + Base58.encode(pubKeyChecksummed);
}
private byte[] calculateChecksum(byte[] data){
byte[] checksum = new byte[160 / 8];
RIPEMD160Digest ripemd160Digest = new RIPEMD160Digest();
ripemd160Digest.update(data, 0, data.length);
ripemd160Digest.doFinal(checksum, 0);
return Arrays.copyOfRange(checksum, 0, 4);
}
}

View file

@ -1,187 +0,0 @@
package cy.agorise.graphenej;
import com.google.common.primitives.UnsignedLong;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import java.lang.reflect.Type;
/**
* Created by nelson on 11/9/16.
*/
public class Asset extends GrapheneObject {
public final static String TAG = "Asset";
public static final String KEY_ID = "id";
public static final String KEY_SYMBOL = "symbol";
public static final String KEY_PRECISION = "precision";
public static final String KEY_ISSUER = "issuer";
public static final String KEY_OPTIONS = "options";
public static final String KEY_MAX_SUPPLY = "max_supply";
public static final String KEY_MARKET_FEE_PERCENT = "market_fee_percent";
public static final String KEY_MARKET_FEE = "max_market_fee";
public static final String KEY_ISSUER_PERMISSIONS = "issuer_permissions";
public static final String KEY_FLAGS = "flags";
public static final String KEY_CORE_EXCHANGE_RATE = "core_exchange_rate";
public static final String KEY_DESCRIPTION = "description";
public static final String KEY_DYNAMIC_ASSET_DATA_ID = "dynamic_asset_data_id";
public static final String KEY_BITASSET_DATA_ID = "bitasset_data_id";
/**
* Enum type used to represent the possible types an asset can be classified into.
*/
public enum AssetType {
CORE_ASSET,
UIA,
SMART_COIN,
PREDICTION_MARKET
}
private String symbol;
private int precision = -1;
private String issuer;
private String description;
private String dynamic_asset_data_id;
private AssetOptions options;
private String bitasset_data_id;
private AssetType mAssetType;
/**
* Simple constructor
* @param id
*/
public Asset(String id) {
super(id);
}
/**
* Constructor
* @param id: The graphene object id.
* @param symbol: The asset symbol.
* @param precision: The asset precision.
*/
public Asset(String id, String symbol, int precision){
super(id);
this.symbol = symbol;
this.precision = precision;
}
/**
* Constructor
* @param id: The graphene object id.
* @param symbol: The asset symbol.
* @param precision: The asset precision.
* @param issuer: Graphene object id of the issuer.
*/
public Asset(String id, String symbol, int precision, String issuer){
super(id);
this.symbol = symbol;
this.precision = precision;
this.issuer = issuer;
}
public String getSymbol(){
return this.symbol;
}
public void setSymbol(String symbol){
this.symbol = symbol;
}
public void setPrecision(int precision){
this.precision = precision;
}
public int getPrecision(){
return this.precision;
}
public void setIssuer(String issuer){ this.issuer = issuer; }
public String getIssuer() { return this.issuer; }
public void setDescription(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
public void setAssetOptions(AssetOptions options){
this.options = options;
}
public AssetOptions getAssetOptions(){
return this.options;
}
public String getBitassetId(){
return this.bitasset_data_id;
}
public void setBitassetDataId(String id){
this.bitasset_data_id = id;
}
public AssetType getAssetType() {
return mAssetType;
}
public void setAssetType(AssetType mAssetType) {
this.mAssetType = mAssetType;
}
@Override
public int hashCode() {
return this.getObjectId() == null ? 0 : this.getObjectId().hashCode();
}
@Override
public boolean equals(Object other) {
if(other instanceof Asset){
return this.getObjectId().equals(((Asset)other).getObjectId());
}else{
return false;
}
}
/**
* Custom deserializer used to instantiate a simple version of the Asset class from the response of the
* 'lookup_asset_symbols' API call.
*/
public static class AssetDeserializer implements JsonDeserializer<Asset> {
@Override
public Asset deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject object = json.getAsJsonObject();
String id = object.get(KEY_ID).getAsString();
String symbol = object.get(KEY_SYMBOL).getAsString();
int precision = object.get(KEY_PRECISION).getAsInt();
String issuer = object.get(KEY_ISSUER).getAsString();
JsonObject optionsJson = object.get(KEY_OPTIONS).getAsJsonObject();
JsonElement bitassetDataId = object.get(KEY_BITASSET_DATA_ID);
// Deserializing asset options
AssetOptions options = new AssetOptions();
options.setMaxSupply(UnsignedLong.valueOf(optionsJson.get(KEY_MAX_SUPPLY).getAsString()));
options.setMarketFeePercent(optionsJson.get(KEY_MARKET_FEE_PERCENT).getAsInt());
options.setMaxMarketFee(UnsignedLong.valueOf(optionsJson.get(KEY_MARKET_FEE).getAsString()));
options.setIssuerPermissions(optionsJson.get(KEY_ISSUER_PERMISSIONS).getAsLong());
options.setFlags(optionsJson.get(KEY_FLAGS).getAsInt());
if(optionsJson.has(KEY_DESCRIPTION))
options.setDescription(optionsJson.get(KEY_DESCRIPTION).getAsString());
//TODO: Deserialize core_exchange_rate field
Asset asset = new Asset(id, symbol, precision, issuer);
asset.setAssetOptions(options);
if(bitassetDataId != null){
asset.setBitassetDataId(bitassetDataId.getAsString());
}
return asset;
}
}
}

View file

@ -1,198 +0,0 @@
package cy.agorise.graphenej;
import com.google.common.math.DoubleMath;
import com.google.common.primitives.Bytes;
import com.google.common.primitives.UnsignedLong;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import java.io.ByteArrayOutputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Type;
import java.math.RoundingMode;
import cy.agorise.graphenej.errors.IncompatibleOperation;
import cy.agorise.graphenej.interfaces.ByteSerializable;
import cy.agorise.graphenej.interfaces.JsonSerializable;
/**
* Created by nelson on 11/7/16.
*/
public class AssetAmount implements ByteSerializable, JsonSerializable {
/**
* Constants used in the JSON serialization procedure.
*/
public static final String KEY_AMOUNT = "amount";
public static final String KEY_ASSET_ID = "asset_id";
private UnsignedLong amount;
private Asset asset;
public AssetAmount(UnsignedLong amount, Asset asset){
this.amount = amount;
this.asset = asset;
}
/**
* Adds two asset amounts. They must refer to the same Asset type.
* @param other: The other AssetAmount to add to this.
* @return: A new instance of the AssetAmount class with the combined amount.
*/
public AssetAmount add(AssetAmount other){
if(!this.getAsset().getObjectId().equals(other.getAsset().getObjectId())){
throw new IncompatibleOperation("Cannot add two AssetAmount instances that refer to different assets");
}
UnsignedLong combined = this.amount.plus(other.getAmount());
return new AssetAmount(combined, asset);
}
/**
* Adds an aditional amount of base units to this AssetAmount.
* @param additional: The amount to add.
* @return: A new instance of the AssetAmount class with the added aditional.
*/
public AssetAmount add(long additional){
UnsignedLong combined = this.amount.plus(UnsignedLong.valueOf(additional));
return new AssetAmount(combined, asset);
}
/**
* Subtracts another instance of AssetAmount from this one. This method will always
* return absolute values.
* @param other: The other asset amount to subtract from this.
* @return: The absolute value of the subtraction of the other minus this asset amount.
*/
public AssetAmount subtract(AssetAmount other){
if(!this.getAsset().getObjectId().equals(other.getAsset().getObjectId())){
throw new IncompatibleOperation("Cannot subtract two AssetAmount instances that refer to different assets");
}
UnsignedLong result = null;
if(this.amount.compareTo(other.getAmount()) < 0){
result = other.getAmount().minus(this.amount);
}else{
result = this.amount.minus(other.getAmount());
}
return new AssetAmount(result, asset);
}
/**
* Multiplies the current amount by a factor provided as the first parameter. The second parameter
* specifies the rounding method to be used.
* @param factor: The multiplying factor
* @param roundingMode: The rounding mode as an instance of the {@link RoundingMode} class
* @return The same AssetAmount instance, but with the changed amount value.
*/
public AssetAmount multiplyBy(double factor, RoundingMode roundingMode){
this.amount = UnsignedLong.valueOf(DoubleMath.roundToLong(this.amount.longValue() * factor, roundingMode));
return this;
}
/**
* Multiplies the current amount by a factor, using the {@link RoundingMode#HALF_DOWN} constant.
* @param factor: The multiplying factor
* @return The same AssetAmount instance, but with the changed amount value.
*/
public AssetAmount multiplyBy(double factor){
return this.multiplyBy(factor, RoundingMode.HALF_DOWN);
}
/**
* Divides the current amount by a divisor provided as the first parameter. The second parameter
* specifies the rounding method to be used.
* @param divisor: The divisor
* @return: The same AssetAMount instance, but with the divided amount value
*/
public AssetAmount dividedBy(double divisor, RoundingMode roundingMode){
this.amount = UnsignedLong.valueOf(DoubleMath.roundToLong(this.amount.longValue() / divisor, roundingMode));
return this;
}
/**
* Divides the current amount by a divisor provided as the first parameter, using
* the {@link RoundingMode#HALF_DOWN} constant
* @param divisor: The divisor
* @return: The same AssetAMount instance, but with the divided amount value
*/
public AssetAmount dividedBy(double divisor){
return this.dividedBy(divisor, RoundingMode.HALF_DOWN);
}
public void setAmount(UnsignedLong amount){
this.amount = amount;
}
public UnsignedLong getAmount(){
return this.amount;
}
public Asset getAsset(){ return this.asset; }
@Override
public byte[] toBytes() {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
DataOutput out = new DataOutputStream(byteArrayOutputStream);
try {
Varint.writeUnsignedVarLong(asset.instance, out);
} catch (IOException e) {
e.printStackTrace();
}
// Getting asset id
byte[] assetId = byteArrayOutputStream.toByteArray();
byte[] value = Util.revertLong(this.amount.longValue());
// Concatenating and returning value + asset id
return Bytes.concat(value, assetId);
}
@Override
public String toJsonString() {
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(AssetAmount.class, new AssetAmountSerializer());
return gsonBuilder.create().toJson(this);
}
@Override
public JsonObject toJsonObject() {
JsonObject jsonAmount = new JsonObject();
jsonAmount.addProperty(KEY_AMOUNT, amount);
jsonAmount.addProperty(KEY_ASSET_ID, asset.getObjectId());
return jsonAmount;
}
/**
* Custom serializer used to translate this object into the JSON-formatted entry we need for a transaction.
*/
public static class AssetAmountSerializer implements JsonSerializer<AssetAmount> {
@Override
public JsonElement serialize(AssetAmount assetAmount, Type type, JsonSerializationContext jsonSerializationContext) {
JsonObject obj = new JsonObject();
obj.addProperty(KEY_AMOUNT, assetAmount.amount);
obj.addProperty(KEY_ASSET_ID, assetAmount.asset.getObjectId());
return obj;
}
}
/**
* Custom deserializer used for this class
*/
public static class AssetAmountDeserializer implements JsonDeserializer<AssetAmount> {
@Override
public AssetAmount deserialize(JsonElement json, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
Long amount = json.getAsJsonObject().get(KEY_AMOUNT).getAsLong();
String assetId = json.getAsJsonObject().get(KEY_ASSET_ID).getAsString();
AssetAmount assetAmount = new AssetAmount(UnsignedLong.valueOf(amount), new Asset(assetId));
return assetAmount;
}
}
}

View file

@ -1,84 +0,0 @@
package cy.agorise.graphenej;
import com.google.common.primitives.UnsignedLong;
/**
* Created by nelson on 12/13/16.
*/
public class AssetOptions {
// TODO: Use these constants instead of using cryptic constants like 128 and 511
public static final int CHARGE_MARKET_FEE = 0x01;
public static final int WHITE_LIST = 0x02;
public static final int OVERRIDE_AUTHORITY = 0x04;
public static final int TRANSFER_RESTRICTED = 0x08;
public static final int DISABLE_FORCE_SETTLE = 0x10;
public static final int GLOBAL_SETTLE = 0x20;
public static final int DISABLE_CONFIDENTIAL = 0x40;
public static final int WITNESS_FED_ASSET = 0x80;
public static final int COMITEE_FED_ASSET = 0x100;
private UnsignedLong max_supply;
private float market_fee_percent;
private UnsignedLong max_market_fee;
private long issuer_permissions;
private int flags;
private Price core_exchange_rate;
//TODO: Implement whitelist_authorities, blacklist_authorities, whitelist_markets, blacklist_markets and extensions
private String description;
public UnsignedLong getMaxSupply() {
return max_supply;
}
public void setMaxSupply(UnsignedLong max_supply) {
this.max_supply = max_supply;
}
public float getMarketFeePercent() {
return market_fee_percent;
}
public void setMarketFeePercent(float market_fee_percent) {
this.market_fee_percent = market_fee_percent;
}
public UnsignedLong getMaxMarketFee() {
return max_market_fee;
}
public void setMaxMarketFee(UnsignedLong max_market_fee) {
this.max_market_fee = max_market_fee;
}
public long getIssuerPermissions() {
return issuer_permissions;
}
public void setIssuerPermissions(long issuer_permissions) {
this.issuer_permissions = issuer_permissions;
}
public int getFlags() {
return flags;
}
public void setFlags(int flags) {
this.flags = flags;
}
public Price getCoreExchangeRate() {
return core_exchange_rate;
}
public void setCoreExchangeRate(Price core_exchange_rate) {
this.core_exchange_rate = core_exchange_rate;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}

View file

@ -1,236 +0,0 @@
package cy.agorise.graphenej;
import com.google.common.primitives.Bytes;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import cy.agorise.graphenej.errors.MalformedAddressException;
import cy.agorise.graphenej.interfaces.GrapheneSerializable;
/**
* Class used to represent the weighted set of keys and accounts that must approve operations.
*
* {@see <a href="https://bitshares.org/doxygen/structgraphene_1_1chain_1_1authority.html">Authority</a>}
*/
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, Long> account_auths;
private HashMap<PublicKey, Long> key_auths;
private Extensions extensions;
public Authority(){
this.weight_threshold = 1;
this.account_auths = new HashMap<UserAccount, Long>();
this.key_auths = new HashMap<PublicKey, Long>();
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, Long> keyAuths, HashMap<UserAccount, Long> accountAuths) {
this();
this.weight_threshold = weight_threshold;
if(keyAuths != null)
this.key_auths = keyAuths;
else
this.key_auths = new HashMap<>();
if(accountAuths != null)
this.account_auths = accountAuths;
else
this.account_auths = new HashMap<>();
}
public long getWeightThreshold() {
return weight_threshold;
}
public void setWeightThreshold(long weight_threshold) {
this.weight_threshold = weight_threshold;
}
public void setKeyAuthorities(HashMap<Address, Long> keyAuths){
if(keyAuths != null){
for(Address address : keyAuths.keySet()){
key_auths.put(address.getPublicKey(), keyAuths.get(address));
}
}
}
public void setAccountAuthorities(HashMap<UserAccount, Long> accountAuthorities){
this.account_auths = accountAuthorities;
}
/**
* @return: Returns a list of public keys linked to this authority
*/
public List<PublicKey> getKeyAuthList(){
ArrayList<PublicKey> keys = new ArrayList<>();
for(PublicKey pk : key_auths.keySet()){
keys.add(pk);
}
return keys;
}
/**
* @return: Returns a list of accounts linked to this authority
*/
public List<UserAccount> getAccountAuthList(){
ArrayList<UserAccount> accounts = new ArrayList<>();
for(UserAccount account : account_auths.keySet()){
accounts.add(account);
}
return accounts;
}
public HashMap<PublicKey, Long> getKeyAuths(){
return this.key_auths;
}
public HashMap<UserAccount, Long> getAccountAuths(){
return this.account_auths;
}
@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);
}
@Override
public boolean equals(Object obj) {
Authority authority = (Authority) obj;
HashMap<PublicKey, Long> keyAuths = authority.getKeyAuths();
HashMap<UserAccount, Long> accountAuths = authority.getAccountAuths();
System.out.println("key auths match: "+this.key_auths.equals(keyAuths));
System.out.println("account auths match: "+this.account_auths.equals(accountAuths));
System.out.println("weight threshold matches: "+(this.weight_threshold == authority.weight_threshold));
return this.key_auths.equals(keyAuths) &&
this.account_auths.equals(accountAuths) &&
this.weight_threshold == authority.weight_threshold;
}
/**
* Custom deserializer used while parsing the 'get_account_by_name' API call response.
*
* This will deserialize an account authority in the form:
*
* {
* "weight_threshold": 1,
* "account_auths": [],
* "key_auths": [["BTS6yoiaoC4p23n31AV4GnMy5QDh5yUQEUmU4PmNxRQPGg7jjPkBq",1]],
* "address_auths": []
* }
*/
public static class AuthorityDeserializer implements JsonDeserializer<Authority> {
@Override
public Authority deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject baseObject = json.getAsJsonObject();
long weightThreshold = baseObject.get(KEY_WEIGHT_THRESHOLD).getAsLong();
JsonArray keyAuthArray = baseObject.getAsJsonArray(KEY_KEY_AUTHS);
JsonArray accountAuthArray = baseObject.getAsJsonArray(KEY_ACCOUNT_AUTHS);
HashMap<PublicKey, Long> keyAuthMap = new HashMap<>();
HashMap<UserAccount, Long> accountAuthMap = new HashMap<>();
for(int i = 0; i < keyAuthArray.size(); i++){
JsonArray subArray = keyAuthArray.get(i).getAsJsonArray();
String addr = subArray.get(0).getAsString();
long weight = subArray.get(1).getAsLong();
try {
keyAuthMap.put(new Address(addr).getPublicKey(), weight);
} catch (MalformedAddressException e) {
System.out.println("MalformedAddressException. Msg: "+e.getMessage());
}
}
for(int i = 0; i < accountAuthArray.size(); i++){
JsonArray subArray = accountAuthArray.get(i).getAsJsonArray();
String userId = subArray.get(0).getAsString();
long weight = subArray.get(1).getAsLong();
UserAccount userAccount = new UserAccount(userId);
accountAuthMap.put(userAccount, weight);
}
return new Authority(weightThreshold, keyAuthMap, accountAuthMap);
}
}
}

View file

@ -1,13 +0,0 @@
package cy.agorise.graphenej;
/**
* Enum-type used to specify the different roles of an authority.
*
* @see <a href="https://bitshares.org/doxygen/authority_8hpp_source.html">Authority</a>
*/
public enum AuthorityType {
OWNER,
ACTIVE,
MEMO
}

View file

@ -1,78 +0,0 @@
package cy.agorise.graphenej;
import java.util.Arrays;
import org.bitcoinj.core.Base58;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.crypto.HDKeyDerivation;
import org.bitcoinj.crypto.MnemonicCode;
import org.spongycastle.crypto.digests.RIPEMD160Digest;
import org.spongycastle.crypto.digests.SHA512Digest;
/**
*
* @author hvarona
*/
public class BIP39 {
private final ECKey mPrivateKey;
public BIP39(String words, String passphrase) {
byte[] seed = MnemonicCode.toSeed(Arrays.asList(words.split(" ")), passphrase);
mPrivateKey = HDKeyDerivation.createMasterPrivateKey(seed);
}
public String getUncompressedAddress() {
RIPEMD160Digest ripemd160Digest = new RIPEMD160Digest();
SHA512Digest sha512Digest = new SHA512Digest();
sha512Digest.update(mPrivateKey.decompress().getPubKey(), 0, mPrivateKey.decompress().getPubKey().length);
byte[] intermediate = new byte[512 / 8];
sha512Digest.doFinal(intermediate, 0);
ripemd160Digest.update(intermediate, 0, intermediate.length);
byte[] output = new byte[160 / 8];
ripemd160Digest.doFinal(output, 0);
String encoded = Base58.encode(output);
byte[] checksum = new byte[(160 / 8) + 4];
System.arraycopy(calculateChecksum(output), 0, checksum, checksum.length - 4, 4);
System.arraycopy(output, 0, checksum, 0, output.length);
return ("BTS" + Base58.encode(checksum));
}
public String getAddress() {
RIPEMD160Digest ripemd160Digest = new RIPEMD160Digest();
SHA512Digest sha512Digest = new SHA512Digest();
sha512Digest.update(mPrivateKey.getPubKey(), 0, mPrivateKey.getPubKey().length);
byte[] intermediate = new byte[512 / 8];
sha512Digest.doFinal(intermediate, 0);
ripemd160Digest.update(intermediate, 0, intermediate.length);
byte[] output = new byte[160 / 8];
ripemd160Digest.doFinal(output, 0);
String encoded = Base58.encode(output);
byte[] checksum = new byte[(160 / 8) + 4];
System.arraycopy(calculateChecksum(output), 0, checksum, checksum.length - 4, 4);
System.arraycopy(output, 0, checksum, 0, output.length);
return ("BTS" + Base58.encode(checksum));
}
public byte[] calculateChecksum(byte[] input) {
byte[] answer = new byte[4];
RIPEMD160Digest ripemd160Digest = new RIPEMD160Digest();
ripemd160Digest.update(input, 0, input.length);
byte[] output = new byte[160 / 8];
ripemd160Digest.doFinal(output, 0);
System.arraycopy(output, 0, answer, 0, 4);
return answer;
}
public byte[] getPublicKey() {
return mPrivateKey.getPubKey();
}
public ECKey getPrivateKey() {
return mPrivateKey;
}
}

View file

@ -1,35 +0,0 @@
package cy.agorise.graphenej;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import cy.agorise.graphenej.interfaces.ByteSerializable;
import cy.agorise.graphenej.interfaces.JsonSerializable;
/**
* Created by nelson on 11/5/16.
*/
public abstract class BaseOperation implements ByteSerializable, JsonSerializable {
public static final String KEY_FEE = "fee";
public static final String KEY_EXTENSIONS = "extensions";
protected OperationType type;
protected Extensions extensions;
public BaseOperation(OperationType type){
this.type = type;
this.extensions = new Extensions();
}
public byte getId() {
return (byte) this.type.ordinal();
}
public abstract void setFee(AssetAmount assetAmount);
public JsonElement toJsonObject(){
JsonArray array = new JsonArray();
array.add(this.getId());
return array;
}
}

View file

@ -1,126 +0,0 @@
package cy.agorise.graphenej;
import cy.agorise.graphenej.interfaces.ByteSerializable;
/**
* This class encapsulates all block-related information needed in order to build a valid transaction.
*/
public class BlockData implements ByteSerializable {
private final int REF_BLOCK_NUM_BYTES = 2;
private final int REF_BLOCK_PREFIX_BYTES = 4;
private final int REF_BLOCK_EXPIRATION_BYTES = 4;
private int refBlockNum;
private long refBlockPrefix;
private long expiration;
/**
* Block data constructor
* @param ref_block_num: Least significant 16 bits from the reference block number.
* If "relative_expiration" is zero, this field must be zero as well.
* @param ref_block_prefix: The first non-block-number 32-bits of the reference block ID.
* Recall that block IDs have 32 bits of block number followed by the
* actual block hash, so this field should be set using the second 32 bits
* in the block_id_type
* @param relative_expiration: Expiration time specified as a POSIX or
* <a href="https://en.wikipedia.org/wiki/Unix_time">Unix time</a>
*/
public BlockData(int ref_block_num, long ref_block_prefix, long relative_expiration){
this.refBlockNum = ref_block_num;
this.refBlockPrefix = ref_block_prefix;
this.expiration = relative_expiration;
}
/**
* Block data constructor that takes in raw blockchain information.
* @param head_block_number: The last block number.
* @param head_block_id: The last block apiId.
* @param relative_expiration: The expiration time.
*/
public BlockData(long head_block_number, String head_block_id, long relative_expiration){
String hashData = head_block_id.substring(8, 16);
StringBuilder builder = new StringBuilder();
for(int i = 0; i < 8; i = i + 2){
builder.append(hashData.substring(6 - i, 8 - i));
}
this.setRefBlockNum(head_block_number);
this.setRefBlockPrefix(head_block_id);
this.expiration = relative_expiration;
}
/**
* Plain setter for the ref_block_num field, assuming its value is exactly the one passed in the argument.
* @param refBlockNum: The 'ref_block_num' field.
*/
public void setRefBlockNum(int refBlockNum) {
this.refBlockNum = refBlockNum;
}
/**
* Setter that receives the block number, and takes the 16 lower bits of it to obtain the
* 'ref_block_num' value.
* @param blockNumber: The block number.
*/
public void setRefBlockNum(long blockNumber){
this.refBlockNum = ((int) blockNumber ) & 0xFFFF;
}
/**
* Plain setter fot the 'ref_block_prefix' field, assumint its value is exactly the one passed in the argument.
* @param refBlockPrefix
*/
public void setRefBlockPrefix(long refBlockPrefix) {
this.refBlockPrefix = refBlockPrefix;
}
/**
* Setter that receives the head block id, and turns it into the little format required for the
* 'ref_block_prefix' field.
* @param headBlockId: The head block id as obtained from the network updates.
*/
public void setRefBlockPrefix(String headBlockId){
String hashData = headBlockId.substring(8, 16);
StringBuilder builder = new StringBuilder();
for(int i = 0; i < 8; i = i + 2){
builder.append(hashData.substring(6 - i, 8 - i));
}
this.refBlockPrefix = Long.parseLong(builder.toString(), 16);
}
public int getRefBlockNum() {
return refBlockNum;
}
public long getRefBlockPrefix() {
return refBlockPrefix;
}
public long getExpiration() {
return expiration;
}
public void setExpiration(long expiration) {
this.expiration = expiration;
}
@Override
public byte[] toBytes() {
// Allocating a fixed length byte array, since we will always need
// 2 bytes for the ref_block_num value
// 4 bytes for the ref_block_prefix value
// 4 bytes for the relative_expiration
byte[] result = new byte[REF_BLOCK_NUM_BYTES + REF_BLOCK_PREFIX_BYTES + REF_BLOCK_EXPIRATION_BYTES];
for(int i = 0; i < result.length; i++){
if(i < REF_BLOCK_NUM_BYTES){
result[i] = (byte) (this.refBlockNum >> 8 * i);
}else if(i >= REF_BLOCK_NUM_BYTES && i < REF_BLOCK_NUM_BYTES + REF_BLOCK_PREFIX_BYTES){
result[i] = (byte) (this.refBlockPrefix >> 8 * (i - REF_BLOCK_NUM_BYTES));
}else{
result[i] = (byte) (this.expiration >> 8 * (i - REF_BLOCK_NUM_BYTES + REF_BLOCK_PREFIX_BYTES));
}
}
return result;
}
}

View file

@ -1,138 +0,0 @@
package cy.agorise.graphenej;
import org.bitcoinj.core.DumpedPrivateKey;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.NetworkParameters;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;
import cy.agorise.graphenej.crypto.SecureRandomGenerator;
/**
* Class used to encapsulate all BrainKey-related operations.
*/
public class BrainKey {
// The size of the word dictionary
public static final int DICT_WORD_COUNT = 49744;
/* The required number of words */
public static final int BRAINKEY_WORD_COUNT = 12;
/* The default sequence number is zero */
public static final int DEFAULT_SEQUENCE_NUMBER = 0;
/* The corresponding private key derivated from the brain key */
private ECKey mPrivateKey;
/* The actual words from this brain key + the sequence number */
private String mBrainKey;
/* The sequence number */
private int sequenceNumber;
/**
* Method that will generate a random brain key
*
* @param words The list of words from the graphene specification
* dictionary.
* @return A random sequence of words
*/
public static String suggest(String words) {
String[] wordArray = words.split(",");
ArrayList<String> suggestedBrainKey = new ArrayList<String>();
assert (wordArray.length == DICT_WORD_COUNT);
SecureRandom secureRandom = SecureRandomGenerator.getSecureRandom();
int index;
for (int i = 0; i < BRAINKEY_WORD_COUNT; i++) {
index = secureRandom.nextInt(DICT_WORD_COUNT - 1);
suggestedBrainKey.add(wordArray[index].toUpperCase());
}
StringBuilder stringBuilder = new StringBuilder();
for(String word : suggestedBrainKey){
stringBuilder.append(word);
stringBuilder.append(" ");
}
return stringBuilder.toString().trim();
}
/**
* BrainKey constructor that takes as argument a specific brain key word
* sequence and generates the private key and address from that.
*
* @param words The brain key specifying the private key
* @param sequence Sequence number
*/
public BrainKey(String words, int sequence) {
this.mBrainKey = words;
this.sequenceNumber = sequence;
String encoded = String.format("%s %d", words, sequence);
try {
MessageDigest md = MessageDigest.getInstance("SHA-512");
byte[] bytes = md.digest(encoded.getBytes("UTF-8"));
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
byte[] result = sha256.digest(bytes);
mPrivateKey = ECKey.fromPrivate(result);
} catch (NoSuchAlgorithmException e) {
System.out.println("NoSuchAlgotithmException. Msg: " + e.getMessage());
} catch (UnsupportedEncodingException e) {
System.out.println("UnsupportedEncodingException. Msg: " + e.getMessage());
}
}
/**
* Gets the array of bytes representing the public key.
* @return
*/
public byte[] getPublicKey() {
return mPrivateKey.getPubKey();
}
/**
* Returns the private key as an instance of the ECKey class.
* @return
*/
public ECKey getPrivateKey() {
return mPrivateKey;
}
/**
* Returns the private key in the Wallet Import Format for the uncompressed private key.
* @see <a href="https://en.bitcoin.it/wiki/Wallet_import_format">WIF</a>
* @return
*/
public String getWalletImportFormat(){
DumpedPrivateKey wif = this.mPrivateKey.decompress().getPrivateKeyEncoded(NetworkParameters.fromID(NetworkParameters.ID_MAINNET));
return wif.toString();
}
/**
* Returns the public address derived from this brain key
* @param prefix: The prefix to use in this address.
* @return An instance of the {@link Address} class
*/
public Address getPublicAddress(String prefix){
return new Address(ECKey.fromPublicOnly(getPublicKey()), prefix);
}
/**
* Brain key words getter
* @return: The word sequence that comprises this brain key
*/
public String getBrainKey(){
return mBrainKey;
}
/**
* Sequence number getter
* @return: The sequence number used alongside with the brain key words in order
* to derive the private key
*/
public int getSequenceNumber(){
return sequenceNumber;
}
}

View file

@ -1,16 +0,0 @@
package cy.agorise.graphenej;
/**
* Created by nelson on 11/8/16.
*/
public class Chains {
public static class BITSHARES {
public static final String CHAIN_ID = "4018d7844c78f6a6c41c6a552b898022310fc5dec06da467ee7905a8dad512c8";
}
public static class GRAPHENE {
public static final String CHAIN_ID = "b8d1603965b3eb1acba27e62ff59f74efa3154d43a4188d381088ac7cdf35539";
}
public static class TEST {
public static final String CHAIN_ID = "39f5e2ede1f8bc1a3a54a7914414e3779e33193f1f5693510e73cb7a87617447";
}
}

View file

@ -1,160 +0,0 @@
package cy.agorise.graphenej;
import java.math.BigDecimal;
import java.math.MathContext;
import cy.agorise.graphenej.errors.IncompleteAssetError;
import cy.agorise.graphenej.models.BucketObject;
/**
* Generic converter class used to translate the market information contained in a BucketObject and/or Price instances.
*
* Created by nelson on 12/23/16.
*/
public class Converter {
private final String TAG = this.getClass().getName();
public static final int OPEN_VALUE = 0;
public static final int CLOSE_VALUE = 1;
public static final int HIGH_VALUE = 2;
public static final int LOW_VALUE = 3;
public static final int BASE_TO_QUOTE = 100;
public static final int QUOTE_TO_BASE = 101;
private Asset base;
private Asset quote;
private BucketObject bucket;
/**
* Constructor meant to be used trying to perform a conversion and in possession of a Price object.
*/
public Converter(){}
/**
* Constructor meant to be used when trying to perform a conversion and in possession of
* a BucketObject, typically resulting from a 'get_market_history' API call.
* @param base
* @param quote
* @param bucket
*/
public Converter(Asset base, Asset quote, BucketObject bucket){
this.base = base;
this.quote = quote;
this.bucket = bucket;
}
/**
* Method used to obtain the equivalence between two assets considering their precisions
* and given the specific time bucket passed in the constructor.
*
* The resulting double value will tell us how much of a given asset, a unit of
* its pair is worth.
*
* The second argument is used to specify which of the assets should
* be taken as a unit reference.
*
* For instance if used with the BASE_TO_QUOTE constant, this method will tell us how
* many of the quote asset will make up for a unit of the base asset. And the opposite
* is true for the QUOTE_TO_BASE contant.
*
* @param bucketAttribute: The desired bucket attribute to take in consideration. Can
* be any of the following: OPEN_VALUE, CLOSE_VALUE, HIGH_VALUE or
* LOW_VALUE.
* @param direction: One of two constants 'BASE_TO_QUOTE' or 'QUOTE_TO_BASE' used to specify
* which of the two assets is the one used as a unitary reference.
* @return: double value representing how much of one asset, a unit of the paired asset
* was worth at the point in time specified by the time bucket and the bucket parameter.
*/
public double getConversionRate(int bucketAttribute, int direction){
if(this.base.getPrecision() == -1 || this.quote.getPrecision() == -1){
throw new IncompleteAssetError();
}
BigDecimal baseValue;
BigDecimal quoteValue;
switch (bucketAttribute){
case OPEN_VALUE:
baseValue = bucket.open_base;
quoteValue = bucket.open_quote;
break;
case CLOSE_VALUE:
baseValue = bucket.close_base;
quoteValue = bucket.close_quote;
break;
case HIGH_VALUE:
baseValue = bucket.high_base;
quoteValue = bucket.high_quote;
break;
case LOW_VALUE:
baseValue = bucket.low_base;
quoteValue = bucket.low_quote;
break;
default:
baseValue = bucket.close_base;
quoteValue = bucket.close_quote;
}
double basePrecisionAdjusted = baseValue.divide(BigDecimal.valueOf((long) Math.pow(10, base.getPrecision()))).doubleValue();
double quotePrecisionAdjusted = quoteValue.divide(BigDecimal.valueOf((long) Math.pow(10, quote.getPrecision()))).doubleValue();
if(direction == QUOTE_TO_BASE){
return basePrecisionAdjusted / quotePrecisionAdjusted;
}else{
return quotePrecisionAdjusted / basePrecisionAdjusted;
}
}
/**
* Converts a given asset amount to the corresponding pair used when creating this class.
* @param assetAmount: The asset to convert from.
* @param bucketAttribute: The bucket attribute to use as a reference. Possible values are OPEN_VALUE,
* CLOSE_VALUE, HIGH_VALUE or LOW_VALUE.
* @return: The converted value in base units, that is the number of a unit x 10^precision
*/
public long convert(AssetAmount assetAmount, int bucketAttribute) {
double conversionRate = 0;
double precisionFactor = 0.0;
if(assetAmount.getAsset().equals(this.base)){
conversionRate = this.getConversionRate(bucketAttribute, BASE_TO_QUOTE);
precisionFactor = Math.pow(10, this.quote.getPrecision()) / Math.pow(10, this.base.getPrecision());
}else if(assetAmount.getAsset().equals(this.quote)){
conversionRate = this.getConversionRate(bucketAttribute, QUOTE_TO_BASE);
precisionFactor = Math.pow(10, this.base.getPrecision()) / Math.pow(10, this.quote.getPrecision());
}
long assetAmountValue = assetAmount.getAmount().longValue();
long convertedBaseValue = (long) (assetAmountValue * conversionRate * precisionFactor);
return convertedBaseValue;
}
/**
* Method used to obtain the conversion rate between two assets given in a Price instance as recovered by the
* 'get_limit_orders' API call.
*
* The same rules that apply for the {@link #getConversionRate(int bucketAttribute, int direction) getConversionRate}
* are valid for the 'direction' argument.
*
* @param price: The Price object instance
* @param direction: The direction from which to perform the conversion, can be only one of BASE_TO_QUOTE or
* QUOTE_TO_BASE.
* @return: A double representing the exchange rate.
*/
public double getConversionRate(Price price, int direction){
Asset base = price.base.getAsset();
Asset quote = price.quote.getAsset();
if(base.getPrecision() == -1 || quote.getPrecision() == -1){
throw new IncompleteAssetError("The given asset instance must provide precision information");
}
double conversionRate = 0;
double precisionFactor = 0.0;
MathContext mathContext = new MathContext(Math.max(base.getPrecision(), quote.getPrecision()));
BigDecimal baseValue = BigDecimal.valueOf(price.base.getAmount().longValue());
BigDecimal quoteValue = BigDecimal.valueOf(price.quote.getAmount().doubleValue());
// System.out.println(String.format("base: %d, quote: %d", baseValue.longValue(), quoteValue.longValue()));
if(direction == BASE_TO_QUOTE){
conversionRate = quoteValue.divide(baseValue, mathContext).doubleValue();
precisionFactor = Math.pow(10, base.getPrecision()) / Math.pow(10, quote.getPrecision());
}else{
conversionRate = baseValue.divide(quoteValue, mathContext).doubleValue();
precisionFactor = Math.pow(10, quote.getPrecision()) / Math.pow(10, base.getPrecision());
}
// System.out.println(String.format("conversion rate: %.4f, precision factor: %.2f", conversionRate, precisionFactor));
return conversionRate * precisionFactor;
}
}

View file

@ -1,43 +0,0 @@
package cy.agorise.graphenej;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import cy.agorise.graphenej.interfaces.ByteSerializable;
import cy.agorise.graphenej.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();
}
}

View file

@ -1,239 +0,0 @@
package cy.agorise.graphenej;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.bitcoinj.core.ECKey;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import cy.agorise.graphenej.crypto.SecureRandomStrengthener;
import cy.agorise.graphenej.models.backup.WalletBackup;
/**
* Class to manage the backup files
*
* @author Henry Varona
* @author Nelson R. Pérez
*/
public abstract class FileBin {
public static final int PUBLIC_KEY_LENGTH = 33;
/**
* Method that receives as input both the bytes from the bin backup and the string used to encrypt the
* data contained in it.
*
* The procedure of deserializing the wallet backup involves first decrypting the data and then decompressing
* it using the LZMA algorithm. Once this two steps are performed, the resulting byte sequence represents
* a JSON-formatted object with one or more wallet and private keys information.
*
* @param input: Input bytes
* @param password: Password used to derive the encryption key
* @return: An instance of the WalletBackup class.
*/
public static WalletBackup deserializeWalletBackup(byte[] input, String password){
try{
byte[] publicKey = new byte[PUBLIC_KEY_LENGTH];
byte[] rawDataEncripted = new byte[input.length - PUBLIC_KEY_LENGTH];
System.arraycopy(input, 0, publicKey, 0, PUBLIC_KEY_LENGTH);
System.arraycopy(input, PUBLIC_KEY_LENGTH, rawDataEncripted, 0, rawDataEncripted.length);
MessageDigest md = MessageDigest.getInstance("SHA-256");
ECKey randomECKey = ECKey.fromPublicOnly(publicKey);
byte[] finalKey = randomECKey.getPubKeyPoint().multiply(ECKey.fromPrivate(md.digest(password.getBytes("UTF-8"))).getPrivKey()).normalize().getXCoord().getEncoded();
MessageDigest md1 = MessageDigest.getInstance("SHA-512");
finalKey = md1.digest(finalKey);
byte[] decryptedData = Util.decryptAES(rawDataEncripted, Util.bytesToHex(finalKey).getBytes());
byte[] checksum = new byte[4];
System.arraycopy(decryptedData, 0, checksum, 0, 4);
byte[] compressedData = new byte[decryptedData.length - 4];
System.arraycopy(decryptedData, 4, compressedData, 0, compressedData.length);
byte[] decompressedData = Util.decompress(compressedData, Util.LZMA);
String walletString = new String(decompressedData, "UTF-8");
System.out.println("Wallet str: "+walletString);
return new GsonBuilder().create().fromJson(walletString, WalletBackup.class);
}catch(NoSuchAlgorithmException e){
System.out.println("NoSuchAlgorithmException. Msg: "+e.getMessage());
} catch (UnsupportedEncodingException e) {
System.out.println("UnsupportedEncodingException. Msg: "+e.getMessage());
}
return null;
}
public static byte[] serializeWalletBackup(WalletBackup walletBackup, String password){
SecureRandomStrengthener randomStrengthener = SecureRandomStrengthener.getInstance();
//randomStrengthener.addEntropySource(new AndroidRandomSource());
SecureRandom secureRandom = randomStrengthener.generateAndSeedRandomNumberGenerator();
try{
String json = new GsonBuilder().create().toJson(walletBackup, WalletBackup.class);
byte[] compressed = Util.compress(json.getBytes(), Util.LZMA);
System.out.println("json: "+json);
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] checksum = md.digest(compressed);
byte[] checksummed = new byte[compressed.length + 4];
System.arraycopy(checksum, 0, checksummed, 0, 4);
System.arraycopy(compressed, 0, checksummed, 4, compressed.length);
byte[] randomKey = new byte[32];
secureRandom.nextBytes(randomKey);
ECKey randomECKey = ECKey.fromPrivate(md.digest(randomKey));
byte[] randPubKey = randomECKey.getPubKey();
byte[] sharedSecret = randomECKey.getPubKeyPoint().multiply(ECKey.fromPrivate(md.digest(password.getBytes("UTF-8"))).getPrivKey()).normalize().getXCoord().getEncoded();
MessageDigest md1 = MessageDigest.getInstance("SHA-512");
byte[] finalKey = md1.digest(sharedSecret);
checksummed = Util.encryptAES(checksummed, Util.byteToString(finalKey).getBytes());
byte[] finalPayload = new byte[checksummed.length + randPubKey.length];
System.arraycopy(randPubKey, 0, finalPayload, 0, randPubKey.length);
System.arraycopy(checksummed, 0, finalPayload, randPubKey.length, checksummed.length);
return finalPayload;
} catch (NoSuchAlgorithmException e) {
System.out.println("NoSuchAlgorithmException. Msg: "+e.getMessage());
} catch (UnsupportedEncodingException e) {
System.out.println("UnsupportedEncodingException. Msg: "+e.getMessage());
}
return null;
}
/**
* Method to get the brainkey fron an input of bytes
*
* @param input Array of bytes of the file to be processed
* @param password the pin code
* @return the brainkey file, or null if the file or the password are
* incorrect
*
* @deprecated use {@link #deserializeWalletBackup(byte[], String)} instead, as it is a more complete method
* that will return a WalletBackup class instance.
*/
@Deprecated
public static String getBrainkeyFromByte(byte[] input, String password) {
try {
byte[] publicKey = new byte[33];
byte[] rawDataEncripted = new byte[input.length - 33];
System.arraycopy(input, 0, publicKey, 0, publicKey.length);
System.arraycopy(input, 33, rawDataEncripted, 0, rawDataEncripted.length);
MessageDigest md = MessageDigest.getInstance("SHA-256");
ECKey randomECKey = ECKey.fromPublicOnly(publicKey);
byte[] finalKey = randomECKey.getPubKeyPoint().multiply(ECKey.fromPrivate(md.digest(password.getBytes("UTF-8"))).getPrivKey()).normalize().getXCoord().getEncoded();
MessageDigest md1 = MessageDigest.getInstance("SHA-512");
finalKey = md1.digest(finalKey);
byte[] rawData = Util.decryptAES(rawDataEncripted, Util.byteToString(finalKey).getBytes());
if(rawData == null) return null;
byte[] checksum = new byte[4];
System.arraycopy(rawData, 0, checksum, 0, 4);
byte[] compressedData = new byte[rawData.length - 4];
System.arraycopy(rawData, 4, compressedData, 0, compressedData.length);
byte[] wallet_object_bytes = Util.decompress(compressedData, Util.XZ);
if(wallet_object_bytes == null) return null;
String wallet_string = new String(wallet_object_bytes, "UTF-8");
JsonObject wallet = new JsonParser().parse(wallet_string).getAsJsonObject();
if (wallet.get("wallet").isJsonArray()) {
wallet = wallet.get("wallet").getAsJsonArray().get(0).getAsJsonObject();
} else {
wallet = wallet.get("wallet").getAsJsonObject();
}
byte[] encKey_enc = new BigInteger(wallet.get("encryption_key").getAsString(), 16).toByteArray();
byte[] temp = new byte[encKey_enc.length - (encKey_enc[0] == 0 ? 1 : 0)];
System.arraycopy(encKey_enc, (encKey_enc[0] == 0 ? 1 : 0), temp, 0, temp.length);
byte[] encKey = Util.decryptAES(temp, password.getBytes("UTF-8"));
temp = new byte[encKey.length];
System.arraycopy(encKey, 0, temp, 0, temp.length);
byte[] encBrain = new BigInteger(wallet.get("encrypted_brainkey").getAsString(), 16).toByteArray();
while (encBrain[0] == 0) {
byte[] temp2 = new byte[encBrain.length - 1];
System.arraycopy(encBrain, 1, temp2, 0, temp2.length);
encBrain = temp2;
}
String BrainKey = new String((Util.decryptAES(encBrain, temp)), "UTF-8");
return BrainKey;
} catch (UnsupportedEncodingException | NoSuchAlgorithmException ex) {
}
return null;
}
/**
* Method to generate the file form a brainkey
*
* @param BrainKey The input brainkey
* @param password The pin code
* @param accountName The Account Name
* @return The array byte of the file, or null if an error happens
*
* @deprecated use {@link #serializeWalletBackup(WalletBackup, String)} instead.
*/
@Deprecated
public static byte[] getBytesFromBrainKey(String BrainKey, String password, String accountName) {
try {
byte[] encKey = new byte[32];
SecureRandomStrengthener randomStrengthener = SecureRandomStrengthener.getInstance();
//randomStrengthener.addEntropySource(new AndroidRandomSource());
SecureRandom secureRandom = randomStrengthener.generateAndSeedRandomNumberGenerator();
secureRandom.nextBytes(encKey);
byte[] encKey_enc = Util.encryptAES(encKey, password.getBytes("UTF-8"));
byte[] encBrain = Util.encryptAES(BrainKey.getBytes("ASCII"), encKey);
/**
* Data to Store
*/
JsonObject wallet = new JsonObject();
wallet.add("encryption_key", new JsonParser().parse(Util.byteToString(encKey_enc)));
wallet.add("encrypted_brainkey", new JsonParser().parse(Util.byteToString(encBrain)));
JsonObject wallet_object = new JsonObject();
wallet_object.add("wallet", wallet);
JsonArray accountNames = new JsonArray();
JsonObject jsonAccountName = new JsonObject();
jsonAccountName.add("name", new JsonParser().parse(accountName));
accountNames.add(jsonAccountName);
wallet_object.add("linked_accounts", accountNames);
byte[] compressedData = Util.compress(wallet_object.toString().getBytes("UTF-8"), Util.XZ);
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] checksum = md.digest(compressedData);
byte[] rawData = new byte[compressedData.length + 4];
System.arraycopy(checksum, 0, rawData, 0, 4);
System.arraycopy(compressedData, 0, rawData, 4, compressedData.length);
byte[] randomKey = new byte[32];
secureRandom.nextBytes(randomKey);
ECKey randomECKey = ECKey.fromPrivate(md.digest(randomKey));
byte[] randPubKey = randomECKey.getPubKey();
byte[] finalKey = randomECKey.getPubKeyPoint().multiply(ECKey.fromPrivate(md.digest(password.getBytes("UTF-8"))).getPrivKey()).normalize().getXCoord().getEncoded();
MessageDigest md1 = MessageDigest.getInstance("SHA-512");
finalKey = md1.digest(finalKey);
rawData = Util.encryptAES(rawData, Util.byteToString(finalKey).getBytes());
byte[] result = new byte[rawData.length + randPubKey.length];
System.arraycopy(randPubKey, 0, result, 0, randPubKey.length);
System.arraycopy(rawData, 0, result, randPubKey.length, rawData.length);
return result;
} catch (UnsupportedEncodingException | NoSuchAlgorithmException ex) {
}
return null;
}
}

View file

@ -1,116 +0,0 @@
package cy.agorise.graphenej;
import com.google.gson.annotations.Expose;
/**
* <p>
* Generic class used to represent a graphene object as defined in
* <a href="http://docs.bitshares.org/development/blockchain/objects.html"></a>
* </p>
* Created by nelson on 11/8/16.
*/
public class GrapheneObject {
public static final String KEY_ID = "id";
public static final int PROTOCOL_SPACE = 1;
public static final int IMPLEMENTATION_SPACE = 2;
@Expose
protected String id;
protected int space;
protected int type;
protected long instance;
public GrapheneObject(String id){
this.id = id;
String[] parts = id.split("\\.");
if(parts.length == 3){
this.space = Integer.parseInt(parts[0]);
this.type = Integer.parseInt(parts[1]);
this.instance = Long.parseLong(parts[2]);
}
}
/**
*
* @return: A String containing the full object apiId in the form {space}.{type}.{instance}
*/
public String getObjectId(){
return String.format("%d.%d.%d", space, type, instance);
}
/**
* Returns the type of this object.
* @return: Instance of the ObjectType enum.
*/
public ObjectType getObjectType(){
switch(space){
case PROTOCOL_SPACE:
switch(type){
case 1:
return ObjectType.BASE_OBJECT;
case 2:
return ObjectType.ACCOUNT_OBJECT;
case 3:
return ObjectType.ASSET_OBJECT;
case 4:
return ObjectType.FORCE_SETTLEMENT_OBJECT;
case 5:
return ObjectType.COMMITTEE_MEMBER_OBJECT;
case 6:
return ObjectType.WITNESS_OBJECT;
case 7:
return ObjectType.LIMIT_ORDER_OBJECT;
case 8:
return ObjectType.CALL_ORDER_OBJECT;
case 9:
return ObjectType.CUSTOM_OBJECT;
case 10:
return ObjectType.PROPOSAL_OBJECT;
case 11:
return ObjectType.OPERATION_HISTORY_OBJECT;
case 12:
return ObjectType.WITHDRAW_PERMISSION_OBJECT;
case 13:
return ObjectType.VESTING_BALANCE_OBJECT;
case 14:
return ObjectType.WORKER_OBJECT;
case 15:
return ObjectType.BALANCE_OBJECT;
}
case IMPLEMENTATION_SPACE:
switch(type){
case 0:
return ObjectType.GLOBAL_PROPERTY_OBJECT;
case 1:
return ObjectType.DYNAMIC_GLOBAL_PROPERTY_OBJECT;
case 3:
return ObjectType.ASSET_DYNAMIC_DATA;
case 4:
return ObjectType.ASSET_BITASSET_DATA;
case 5:
return ObjectType.ACCOUNT_BALANCE_OBJECT;
case 6:
return ObjectType.ACCOUNT_STATISTICS_OBJECT;
case 7:
return ObjectType.TRANSACTION_OBJECT;
case 8:
return ObjectType.BLOCK_SUMMARY_OBJECT;
case 9:
return ObjectType.ACCOUNT_TRANSACTION_HISTORY_OBJECT;
case 10:
return ObjectType.BLINDED_BALANCE_OBJECT;
case 11:
return ObjectType.CHAIN_PROPERTY_OBJECT;
case 12:
return ObjectType.WITNESS_SCHEDULE_OBJECT;
case 13:
return ObjectType.BUDGET_RECORD_OBJECT;
case 14:
return ObjectType.SPECIAL_AUTHORITY_OBJECT;
}
}
return null;
}
}

View file

@ -1,111 +0,0 @@
package cy.agorise.graphenej;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import org.bitcoinj.core.Base58;
import cy.agorise.graphenej.interfaces.JsonSerializable;
/**
* Class used to handle invoice generation, compression and QR-Code data derivation,
* as detailed in <a href="http://docs.bitshares.eu/integration/merchants/merchant-protocol.html">this</> link.
* @author Nelson R. Pérez
*/
public class Invoice implements JsonSerializable {
private String to;
private String to_label;
private String memo;
private String currency;
private LineItem[] line_items;
private String note;
private String callback;
public Invoice(String to, String to_label, String memo, String currency, LineItem[] items, String note, String callback){
this.to = to;
this.to_label = to_label;
this.memo = memo;
this.currency = currency;
this.line_items = items;
this.note = note;
this.callback = callback;
}
public String getToLabel() {
return to_label;
}
public void setToLabel(String to_label) {
this.to_label = to_label;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
public String getMemo() {
return memo;
}
public void setMemo(String memo) {
this.memo = memo;
}
public String getCurrency() {
return currency;
}
public void setCurrency(String currency) {
this.currency = currency;
}
public LineItem[] getLineItems() {
return line_items;
}
public void setLineItems(LineItem[] line_items) {
this.line_items = line_items;
}
public String getCallback() {
return callback;
}
public void setCallback(String callback) {
this.callback = callback;
}
@Override
public String toJsonString() {
Gson gson = new Gson();
return gson.toJson(this);
}
@Override
public JsonElement toJsonObject() {
return null;
}
public static String toQrCode(Invoice invoice){
String json = invoice.toJsonString();
return Base58.encode(Util.compress(json.getBytes(), Util.LZMA));
}
public static Invoice fromQrCode(String encoded){
String json = new String(Util.decompress(Base58.decode(encoded), Util.LZMA));
Gson gson = new Gson();
return gson.fromJson(json, Invoice.class);
}
}

View file

@ -1,136 +0,0 @@
package cy.agorise.graphenej;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import java.io.ByteArrayOutputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Type;
import cy.agorise.graphenej.interfaces.ByteSerializable;
/**
*
* @author henry
*/
public class LimitOrder extends GrapheneObject implements ByteSerializable {
public static final String KEY_EXPIRATION = "expiration";
public static final String KEY_SELLER = "seller";
public static final String KEY_FOR_SALE = "for_sale";
public static final String KEY_DEFERRED_FEE = "deferred_fee";
public static final String KEY_PRICE = "sell_price";
private String expiration;
private UserAccount seller;
private long forSale;
private long deferredFee;
private Price sellPrice;
public LimitOrder(String id) {
super(id);
}
public String getExpiration() {
return expiration;
}
public void setExpiration(String expiration) {
this.expiration = expiration;
}
public UserAccount getSeller() {
return seller;
}
public void setSeller(UserAccount seller) {
this.seller = seller;
}
public long getForSale() {
return forSale;
}
public void setForSale(long forSale) {
this.forSale = forSale;
}
public long getDeferredFee() {
return deferredFee;
}
public void setDeferredFee(long deferredFee) {
this.deferredFee = deferredFee;
}
public Price getSellPrice() {
return sellPrice;
}
public void setSellPrice(Price sellPrice) {
this.sellPrice = sellPrice;
}
@Override
public byte[] toBytes() {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
DataOutput out = new DataOutputStream(byteArrayOutputStream);
byte[] serialized = null;
try {
Varint.writeUnsignedVarLong(this.instance, out);
serialized = byteArrayOutputStream.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return serialized;
}
/**
* Custom deserializer for the LimitOrder class, used to deserialize a json-formatted string in
* the following format:
*
* {
* "id": "1.7.2389233",
* "expiration": "2017-04-21T15:40:04",
* "seller": "1.2.114363",
* "forSale": "10564959415",
* "sell_price": {
* "base": {
* "amount": "10565237932",
* "asset_id": "1.3.0"
* },
* "quote": {
* "amount": 5803878,
* "asset_id": "1.3.121"
* }
* },
* "deferredFee": 0
* }
*/
public static class LimitOrderDeserializer implements JsonDeserializer<LimitOrder> {
@Override
public LimitOrder deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject object = json.getAsJsonObject();
String id = object.get(KEY_ID).getAsString();
String expiration = object.get(KEY_EXPIRATION).getAsString();
UserAccount seller = context.deserialize(object.get(KEY_SELLER), UserAccount.class);
String forSale = object.get(KEY_FOR_SALE).getAsString();
Price price = context.deserialize(object.get(KEY_PRICE), Price.class);
long deferredFee = object.get(KEY_DEFERRED_FEE).getAsLong();
LimitOrder limitOrder = new LimitOrder(id);
limitOrder.setExpiration(expiration);
limitOrder.setSeller(seller);
limitOrder.setForSale(Long.parseLong(forSale));
limitOrder.setSellPrice(price);
limitOrder.setDeferredFee(deferredFee);
return limitOrder;
}
}
}

View file

@ -1,40 +0,0 @@
package cy.agorise.graphenej;
/**
* Created by nelson on 1/11/17.
*/
public class LineItem {
private String label;
private int quantity;
private double price;
public LineItem(String label, int quantity, double price){
this.label = label;
this.quantity = quantity;
this.price = price;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getLabel(){
return label;
}
public void setLabel(String label) {
this.label = label;
}
}

View file

@ -1,12 +0,0 @@
package cy.agorise.graphenej;
/**
*
* @author henry
*/
public class MarketTrade {
public String date;
public double price;
public double amount;
public double value;
}

View file

@ -1,182 +0,0 @@
package cy.agorise.graphenej;
/**
* Enum type used to list all possible object types and obtain their space + type id
*/
public enum ObjectType {
BASE_OBJECT,
ACCOUNT_OBJECT,
ASSET_OBJECT,
FORCE_SETTLEMENT_OBJECT,
COMMITTEE_MEMBER_OBJECT,
WITNESS_OBJECT,
LIMIT_ORDER_OBJECT,
CALL_ORDER_OBJECT,
CUSTOM_OBJECT,
PROPOSAL_OBJECT,
OPERATION_HISTORY_OBJECT,
WITHDRAW_PERMISSION_OBJECT,
VESTING_BALANCE_OBJECT,
WORKER_OBJECT,
BALANCE_OBJECT,
GLOBAL_PROPERTY_OBJECT,
DYNAMIC_GLOBAL_PROPERTY_OBJECT,
ASSET_DYNAMIC_DATA,
ASSET_BITASSET_DATA,
ACCOUNT_BALANCE_OBJECT,
ACCOUNT_STATISTICS_OBJECT,
TRANSACTION_OBJECT,
BLOCK_SUMMARY_OBJECT,
ACCOUNT_TRANSACTION_HISTORY_OBJECT,
BLINDED_BALANCE_OBJECT,
CHAIN_PROPERTY_OBJECT,
WITNESS_SCHEDULE_OBJECT,
BUDGET_RECORD_OBJECT,
SPECIAL_AUTHORITY_OBJECT;
private int getSpace(){
int space = 1;
switch(this){
case BASE_OBJECT:
case ACCOUNT_OBJECT:
case ASSET_OBJECT:
case FORCE_SETTLEMENT_OBJECT:
case COMMITTEE_MEMBER_OBJECT:
case WITNESS_OBJECT:
case LIMIT_ORDER_OBJECT:
case CALL_ORDER_OBJECT:
case CUSTOM_OBJECT:
case PROPOSAL_OBJECT:
case OPERATION_HISTORY_OBJECT:
case WITHDRAW_PERMISSION_OBJECT:
case VESTING_BALANCE_OBJECT:
case WORKER_OBJECT:
case BALANCE_OBJECT:
space = 1;
break;
case GLOBAL_PROPERTY_OBJECT:
case DYNAMIC_GLOBAL_PROPERTY_OBJECT:
case ASSET_DYNAMIC_DATA:
case ASSET_BITASSET_DATA:
case ACCOUNT_BALANCE_OBJECT:
case ACCOUNT_STATISTICS_OBJECT:
case TRANSACTION_OBJECT:
case BLOCK_SUMMARY_OBJECT:
case ACCOUNT_TRANSACTION_HISTORY_OBJECT:
case BLINDED_BALANCE_OBJECT:
case CHAIN_PROPERTY_OBJECT:
case WITNESS_SCHEDULE_OBJECT:
case BUDGET_RECORD_OBJECT:
case SPECIAL_AUTHORITY_OBJECT:
space = 2;
break;
}
return space;
}
private int getType(){
int type = 0;
switch(this){
case BASE_OBJECT:
type = 1;
break;
case ACCOUNT_OBJECT:
type = 2;
break;
case ASSET_OBJECT:
type = 3;
break;
case FORCE_SETTLEMENT_OBJECT:
type = 4;
break;
case COMMITTEE_MEMBER_OBJECT:
type = 5;
break;
case WITNESS_OBJECT:
type = 6;
break;
case LIMIT_ORDER_OBJECT:
type = 7;
break;
case CALL_ORDER_OBJECT:
type = 8;
break;
case CUSTOM_OBJECT:
type = 9;
break;
case PROPOSAL_OBJECT:
type = 10;
break;
case OPERATION_HISTORY_OBJECT:
type = 11;
break;
case WITHDRAW_PERMISSION_OBJECT:
type = 12;
break;
case VESTING_BALANCE_OBJECT:
type = 13;
break;
case WORKER_OBJECT:
type = 14;
break;
case BALANCE_OBJECT:
type = 15;
break;
case GLOBAL_PROPERTY_OBJECT:
type = 0;
break;
case DYNAMIC_GLOBAL_PROPERTY_OBJECT:
type = 1;
break;
case ASSET_DYNAMIC_DATA:
type = 3;
break;
case ASSET_BITASSET_DATA:
type = 4;
break;
case ACCOUNT_BALANCE_OBJECT:
type = 5;
break;
case ACCOUNT_STATISTICS_OBJECT:
type = 6;
break;
case TRANSACTION_OBJECT:
type = 7;
break;
case BLOCK_SUMMARY_OBJECT:
type = 8;
break;
case ACCOUNT_TRANSACTION_HISTORY_OBJECT:
type = 9;
break;
case BLINDED_BALANCE_OBJECT:
type = 10;
break;
case CHAIN_PROPERTY_OBJECT:
type = 11;
break;
case WITNESS_SCHEDULE_OBJECT:
type = 12;
break;
case BUDGET_RECORD_OBJECT:
type = 13;
break;
case SPECIAL_AUTHORITY_OBJECT:
type = 14;
}
return type;
}
/**
* This method is used to return the generic object type in the form space.type.0.
*
* Not to be confused with {@link GrapheneObject#getObjectId()}, which will return
* the full object id in the form space.type.id.
*
* @return: The generic object type
*/
public String getGenericObjectId(){
return String.format("%d.%d.0", getSpace(), getType());
}
}

View file

@ -1,55 +0,0 @@
package cy.agorise.graphenej;
/**
* Enum type used to keep track of all the operation types and their corresponding ids.
*
* <a href="https://bitshares.org/doxygen/operations_8hpp_source.html">Source</a>
*
* Created by nelson on 11/6/16.
*/
public enum OperationType {
TRANSFER_OPERATION,
LIMIT_ORDER_CREATE_OPERATION,
LIMIT_ORDER_CANCEL_OPERATION,
CALL_ORDER_UPDATE_OPERATION,
FILL_ORDER_OPERATION, // VIRTUAL
ACCOUNT_CREATE_OPERATION,
ACCOUNT_UPDATE_OPERATION,
ACCOUNT_WHITELIST_OPERATION,
ACCOUNT_UPGRADE_OPERATION,
ACCOUNT_TRANSFER_OPERATION,
ASSET_CREATE_OPERATION,
ASSET_UPDATE_OPERATION,
ASSET_UPDATE_BITASSET_OPERATION,
ASSET_UPDATE_FEED_PRODUCERS_OPERATION,
ASSET_ISSUE_OPERATION,
ASSET_RESERVE_OPERATION,
ASSET_FUND_FEE_POOL_OPERATION,
ASSET_SETTLE_OPERATION,
ASSET_GLOBAL_SETTLE_OPERATION,
ASSET_PUBLISH_FEED_OPERATION,
WITNESS_CREATE_OPERATION,
WITNESS_UPDATE_OPERATION,
PROPOSAL_CREATE_OPERATION,
PROPOSAL_UPDATE_OPERATION,
PROPOSAL_DELETE_OPERATION,
WITHDRAW_PERMISSION_CREATE_OPERATION,
WITHDRAW_PERMISSION_UPDATE_OPERATION,
WITHDRAW_PERMISSION_CLAIM_OPERATION,
WITHDRAW_PERMISSION_DELETE_OPERATION,
COMMITTEE_MEMBER_CREATE_OPERATION,
COMMITTEE_MEMBER_UPDATE_OPERATION,
COMMITTEE_MEMBER_UPDATE_GLOBAL_PARAMETERS_OPERATION,
VESTING_BALANCE_CREATE_OPERATION,
VESTING_BALANCE_WITHDRAW_OPERATION,
WORKER_CREATE_OPERATION,
CUSTOM_OPERATION,
ASSERT_OPERATION,
BALANCE_CLAIM_OPERATION,
OVERRIDE_TRANSFER_OPERATION,
TRANSFER_TO_BLIND_OPERATION,
BLIND_TRANSFER_OPERATION,
TRANSFER_FROM_BLIND_OPERATION,
ASSET_SETTLE_CANCEL_OPERATION, // VIRTUAL
ASSET_CLAIM_FEES_OPERATION
}

View file

@ -1,46 +0,0 @@
package cy.agorise.graphenej;
import com.google.gson.JsonElement;
import cy.agorise.graphenej.interfaces.ByteSerializable;
import cy.agorise.graphenej.interfaces.GrapheneSerializable;
/**
* Container template class used whenever we have an optional field.
*
* The idea here is that the binary serialization of this field should be performed
* in a specific way determined by the field implementing the {@link ByteSerializable}
* interface, more specifically using the {@link ByteSerializable#toBytes()} method.
*
* However, if the field is missing, the Optional class should be able to know how
* to serialize it, as this is always done by placing an zero byte.
*/
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();
}
public boolean isSet(){
return this.optionalField != null;
}
@Override
public String toJsonString() {
return optionalField.toJsonString();
}
@Override
public JsonElement toJsonObject() {
return optionalField.toJsonObject();
}
}

View file

@ -1,98 +0,0 @@
package cy.agorise.graphenej;
import com.google.common.math.DoubleMath;
import com.google.common.primitives.UnsignedLong;
import java.math.RoundingMode;
import java.util.List;
import cy.agorise.graphenej.operations.LimitOrderCreateOperation;
/**
* This class will maintain a snapshot of the order book between two assets.
*
* It also provides a handy method that should return the appropriate LimitOrderCreateOperation
* object needed in case the user wants to perform market-priced operations.
*
* It is important to keep the order book updated, ideally by listening to blockchain events, and calling the 'update' method.
*
* Created by nelson on 3/25/17.
*/
public class OrderBook {
private List<LimitOrder> limitOrders;
public OrderBook(List<LimitOrder> limitOrders){
this.limitOrders = limitOrders;
}
/**
* Replaces the current limit order by the list provided as parameter.
* @param limitOrders: New list of orders
*/
public void update(List<LimitOrder> limitOrders){
this.limitOrders = limitOrders;
}
public void update(LimitOrder limitOrder){
//TODO: Implement the method that will update a single limit order from the order book
}
/**
* High level method used to exchange a specific amount of an asset (The base) for another
* one (The quote) at market value.
*
* It should analyze the order book and figure out the optimal amount of the base asset to give
* away in order to obtain the desired amount of the quote asset.
*
* @param seller: User account of the seller, used to build the limit order create operation
* @param myBaseAsset: The asset the user is willing to give away
* @param myQuoteAmount: The amount of a given asset the user wants
* @param expiration: The expiration time of the limit order
*
* @return An instance of the LimitOrderCreateOperation class, which is ready to be broadcasted.
*/
public LimitOrderCreateOperation exchange(UserAccount seller, Asset myBaseAsset, AssetAmount myQuoteAmount, int expiration){
AssetAmount toSell = new AssetAmount(UnsignedLong.valueOf(calculateRequiredBase(myQuoteAmount)), myBaseAsset);
AssetAmount toReceive = myQuoteAmount;
LimitOrderCreateOperation buyOrder = new LimitOrderCreateOperation(seller, toSell, toReceive, expiration, true);
return buyOrder;
}
/**
* Given a specific amount of a desired asset, this method will calculate how much of the corresponding
* asset we need to offer to perform a successful transaction with the current order book.
* @param quoteAmount: The amount of the desired asset.
* @return: The minimum amount of the base asset that we need to give away
*/
public long calculateRequiredBase(AssetAmount quoteAmount){
long totalBought = 0;
long totalSold = 0;
for(int i = 0; i < this.limitOrders.size() && totalBought < quoteAmount.getAmount().longValue(); i++){
LimitOrder order = this.limitOrders.get(i);
// If the base asset is the same as our quote asset, we have a match
if(order.getSellPrice().base.getAsset().getObjectId().equals(quoteAmount.getAsset().getObjectId())){
// My quote amount, is the order's base amount
long orderAmount = order.getForSale();
// The amount of the quote asset we still need
long stillNeed = quoteAmount.getAmount().longValue() - totalBought;
// If the offered amount is greater than what we still need, we exchange just what we need
if(orderAmount >= stillNeed) {
totalBought += stillNeed;
double additionalRatio = (double) stillNeed / (double) order.getSellPrice().base.getAmount().longValue();
double additionalAmount = order.getSellPrice().quote.getAmount().longValue() * additionalRatio;
long longAdditional = DoubleMath.roundToLong(additionalAmount, RoundingMode.HALF_UP);
totalSold += longAdditional;
}else{
// If the offered amount is less than what we need, we exchange the whole order
totalBought += orderAmount;
totalSold += order.getSellPrice().quote.getAmount().longValue();
}
}
}
return totalSold;
}
}

View file

@ -1,28 +0,0 @@
package cy.agorise.graphenej;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import java.lang.reflect.Type;
/**
* The price struct stores asset prices in the Graphene system.
*
* A price is defined as a ratio between two assets, and represents a possible exchange rate
* between those two assets. prices are generally not stored in any simplified form, i.e. a price
* of (1000 CORE)/(20 USD) is perfectly normal.
* The assets within a price are labeled base and quote. Throughout the Graphene code base,
* the convention used is that the base asset is the asset being sold, and the quote asset is
* the asset being purchased, where the price is represented as base/quote, so in the example
* price above the seller is looking to sell CORE asset and get USD in return.
*
* Note: Taken from the Graphene doxygen.
* Created by nelson on 12/16/16.
*/
public class Price {
public AssetAmount base;
public AssetAmount quote;
}

View file

@ -1,56 +0,0 @@
package cy.agorise.graphenej;
import org.bitcoinj.core.ECKey;
import org.spongycastle.math.ec.ECPoint;
import java.io.Serializable;
import cy.agorise.graphenej.interfaces.ByteSerializable;
/**
* Created by nelson on 11/30/16.
*/
public class PublicKey implements ByteSerializable, Serializable {
private ECKey publicKey;
public PublicKey(ECKey key) {
if(key.hasPrivKey()){
throw new IllegalStateException("Passing a private key to PublicKey constructor");
}
this.publicKey = key;
}
public ECKey getKey(){
return publicKey;
}
@Override
public byte[] toBytes() {
if(publicKey.isCompressed()) {
return publicKey.getPubKey();
}else{
publicKey = ECKey.fromPublicOnly(ECKey.compressPoint(publicKey.getPubKeyPoint()));
return publicKey.getPubKey();
}
}
public String getAddress(){
ECKey pk = ECKey.fromPublicOnly(publicKey.getPubKey());
if(!pk.isCompressed()){
ECPoint point = ECKey.compressPoint(pk.getPubKeyPoint());
pk = ECKey.fromPublicOnly(point);
}
return new Address(pk).toString();
}
@Override
public int hashCode() {
return publicKey.hashCode();
}
@Override
public boolean equals(Object obj) {
PublicKey other = (PublicKey) obj;
return this.publicKey.equals(other.getKey());
}
}

View file

@ -1,32 +0,0 @@
package cy.agorise.graphenej;
/**
* Created by nelson on 11/16/16.
*/
public class RPC {
public static final String VERSION = "2.0";
public static final String CALL_LOGIN = "login";
public static final String CALL_NETWORK_BROADCAST = "network_broadcast";
public static final String CALL_HISTORY = "history";
public static final String CALL_DATABASE = "database";
public static final String CALL_ASSET = "asset";
public static final String CALL_SET_SUBSCRIBE_CALLBACK = "set_subscribe_callback";
public static final String CALL_CANCEL_ALL_SUBSCRIPTIONS = "cancel_all_subscriptions";
public static final String CALL_GET_ACCOUNT_BY_NAME = "get_account_by_name";
public static final String CALL_GET_ACCOUNTS = "get_accounts";
public static final String CALL_GET_DYNAMIC_GLOBAL_PROPERTIES = "get_dynamic_global_properties";
public static final String CALL_BROADCAST_TRANSACTION = "broadcast_transaction";
public static final String CALL_GET_REQUIRED_FEES = "get_required_fees";
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_LOOKUP_ACCOUNTS = "lookup_accounts";
public static final String CALL_LIST_ASSETS = "list_assets";
public static final String GET_OBJECTS = "get_objects";
public static final String GET_ACCOUNT_BALANCES = "get_account_balances";
public static final String CALL_LOOKUP_ASSET_SYMBOLS = "lookup_asset_symbols";
public static final String CALL_GET_BLOCK_HEADER = "get_block_header";
public static final String CALL_GET_LIMIT_ORDERS = "get_limit_orders";
public static final String CALL_GET_TRADE_HISTORY = "get_trade_history";
public static final String CALL_GET_MARKET_HISTORY = "get_market_history";
public static final String CALL_GET_ALL_ASSET_HOLDERS = "get_all_asset_holders";
}

View file

@ -1,377 +0,0 @@
package cy.agorise.graphenej;
import com.google.common.primitives.Bytes;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import org.bitcoinj.core.DumpedPrivateKey;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.Sha256Hash;
import org.bitcoinj.core.Utils;
import java.lang.reflect.Type;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import cy.agorise.graphenej.interfaces.ByteSerializable;
import cy.agorise.graphenej.interfaces.JsonSerializable;
import cy.agorise.graphenej.operations.LimitOrderCreateOperation;
import cy.agorise.graphenej.operations.TransferOperation;
/**
* Class used to represent a generic Graphene transaction.
*/
public class Transaction implements ByteSerializable, JsonSerializable {
/* Default expiration time */
public static final int DEFAULT_EXPIRATION_TIME = 30;
/* Constant field names used for serialization/deserialization purposes */
public static final String KEY_EXPIRATION = "expiration";
public static final String KEY_SIGNATURES = "signatures";
public static final String KEY_OPERATIONS = "operations";
public static final String KEY_EXTENSIONS = "extensions";
public static final String KEY_REF_BLOCK_NUM = "ref_block_num";
public static final String KEY_REF_BLOCK_PREFIX = "ref_block_prefix";
private ECKey privateKey;
private BlockData blockData;
private List<BaseOperation> operations;
private Extensions extensions;
/**
* Transaction constructor.
* @param privateKey : Instance of a ECKey containing the private key that will be used to sign this transaction.
* @param blockData : Block data containing important information used to sign a transaction.
* @param operationList : List of operations to include in the transaction.
*/
public Transaction(ECKey privateKey, BlockData blockData, List<BaseOperation> operationList){
this.privateKey = privateKey;
this.blockData = blockData;
this.operations = operationList;
this.extensions = new 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);
}
/**
* Constructor used to build a Transaction object without a private key. This kind of object
* is used to represent a transaction data that we don't intend to serialize and sign.
* @param blockData: Block data instance, containing information about the location of this transaction in the blockchain.
* @param operationList: The list of operations included in this transaction.
*/
public Transaction(BlockData blockData, List<BaseOperation> operationList){
this.blockData = blockData;
this.operations = operationList;
}
/**
* 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(){
return this.privateKey;
}
public List<BaseOperation> getOperations(){ return this.operations; }
/**
* This method is used to query whether the instance has a private key.
* @return
*/
public boolean hasPrivateKey(){
return this.privateKey != null;
}
/**
* Obtains a signature of this transaction. Please note that due to the current reliance on
* bitcoinj to generate the signatures, and due to the fact that it uses deterministic
* ecdsa signatures, we are slightly modifying the expiration time of the transaction while
* we look for a signature that will be accepted by the graphene network.
*
* This should then be called before any other serialization method.
* @return: A valid signature of the current transaction.
*/
public byte[] getGrapheneSignature(){
boolean isGrapheneCanonical = false;
byte[] sigData = null;
while(!isGrapheneCanonical) {
byte[] serializedTransaction = this.toBytes();
Sha256Hash hash = Sha256Hash.wrap(Sha256Hash.hash(serializedTransaction));
int recId = -1;
ECKey.ECDSASignature sig = privateKey.sign(hash);
// Now we have to work backwards to figure out the recId needed to recover the signature.
for (int i = 0; i < 4; i++) {
ECKey k = ECKey.recoverFromSignature(i, sig, hash, privateKey.isCompressed());
if (k != null && k.getPubKeyPoint().equals(privateKey.getPubKeyPoint())) {
recId = i;
break;
}
}
sigData = new byte[65]; // 1 header + 32 bytes for R + 32 bytes for S
int headerByte = recId + 27 + (privateKey.isCompressed() ? 4 : 0);
sigData[0] = (byte) headerByte;
System.arraycopy(Utils.bigIntegerToBytes(sig.r, 32), 0, sigData, 1, 32);
System.arraycopy(Utils.bigIntegerToBytes(sig.s, 32), 0, sigData, 33, 32);
// Further "canonicality" tests
if(((sigData[0] & 0x80) != 0) || (sigData[0] == 0) ||
((sigData[1] & 0x80) != 0) || ((sigData[32] & 0x80) != 0) ||
(sigData[32] == 0) || ((sigData[33] & 0x80) != 0)){
this.blockData.setExpiration(this.blockData.getExpiration() + 1);
}else{
isGrapheneCanonical = true;
}
}
return sigData;
}
/**
* Method that creates a serialized byte array with compact information about this transaction
* that is needed for the creation of a signature.
* @return: byte array with serialized information about this transaction.
*/
public byte[] toBytes(){
// Creating a List of Bytes and adding the first bytes from the chain apiId
List<Byte> byteArray = new ArrayList<Byte>();
byteArray.addAll(Bytes.asList(Util.hexToBytes(Chains.BITSHARES.CHAIN_ID)));
// Adding the block data
byteArray.addAll(Bytes.asList(this.blockData.toBytes()));
// Adding the number of operations
byteArray.add((byte) this.operations.size());
// Adding all the operations
for(BaseOperation operation : operations){
byteArray.add(operation.getId());
byteArray.addAll(Bytes.asList(operation.toBytes()));
}
// Adding extensions byte
byteArray.addAll(Bytes.asList(this.extensions.toBytes()));
return Bytes.toArray(byteArray);
}
@Override
public String toJsonString() {
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Transaction.class, new TransactionSerializer());
return gsonBuilder.create().toJson(this);
}
@Override
public JsonObject toJsonObject() {
JsonObject obj = new JsonObject();
// Getting the signature before anything else,
// since this might change the transaction expiration data slightly
byte[] signature = getGrapheneSignature();
// Formatting expiration time
Date expirationTime = new Date(blockData.getExpiration() * 1000);
SimpleDateFormat dateFormat = new SimpleDateFormat(Util.TIME_DATE_FORMAT);
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
// Adding expiration
obj.addProperty(KEY_EXPIRATION, dateFormat.format(expirationTime));
// Adding signatures
JsonArray signatureArray = new JsonArray();
signatureArray.add(Util.bytesToHex(signature));
obj.add(KEY_SIGNATURES, signatureArray);
JsonArray operationsArray = new JsonArray();
for(BaseOperation operation : operations){
operationsArray.add(operation.toJsonObject());
}
// Adding operations
obj.add(KEY_OPERATIONS, operationsArray);
// Adding extensions
obj.add(KEY_EXTENSIONS, new JsonArray());
// Adding block data
obj.addProperty(KEY_REF_BLOCK_NUM, blockData.getRefBlockNum());
obj.addProperty(KEY_REF_BLOCK_PREFIX, blockData.getRefBlockPrefix());
return obj;
}
/**
* Class used to encapsulate the procedure to be followed when converting a transaction from a
* java object to its JSON string format representation.
*/
public static class TransactionSerializer implements JsonSerializer<Transaction> {
@Override
public JsonElement serialize(Transaction transaction, Type type, JsonSerializationContext jsonSerializationContext) {
return transaction.toJsonObject();
}
}
/**
* Static inner class used to encapsulate the procedure to be followed when converting a transaction from its
* JSON string format representation into a java object instance.
*/
public static class TransactionDeserializer implements JsonDeserializer<Transaction> {
@Override
public Transaction deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
// Parsing block data information
int refBlockNum = jsonObject.get(KEY_REF_BLOCK_NUM).getAsInt();
long refBlockPrefix = jsonObject.get(KEY_REF_BLOCK_PREFIX).getAsLong();
String expiration = jsonObject.get(KEY_EXPIRATION).getAsString();
SimpleDateFormat dateFormat = new SimpleDateFormat(Util.TIME_DATE_FORMAT);
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
Date expirationDate = dateFormat.parse(expiration, new ParsePosition(0));
BlockData blockData = new BlockData(refBlockNum, refBlockPrefix, expirationDate.getTime());
// Parsing operation list
BaseOperation operation = null;
ArrayList<BaseOperation> operationList = new ArrayList<>();
try {
for (JsonElement jsonOperation : jsonObject.get(KEY_OPERATIONS).getAsJsonArray()) {
int operationId = jsonOperation.getAsJsonArray().get(0).getAsInt();
if (operationId == OperationType.TRANSFER_OPERATION.ordinal()) {
operation = context.deserialize(jsonOperation, TransferOperation.class);
} else if (operationId == OperationType.LIMIT_ORDER_CREATE_OPERATION.ordinal()) {
operation = context.deserialize(jsonOperation, LimitOrderCreateOperation.class);
} else if (operationId == OperationType.LIMIT_ORDER_CANCEL_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.CALL_ORDER_UPDATE_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.FILL_ORDER_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.ACCOUNT_CREATE_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.ACCOUNT_UPDATE_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.ACCOUNT_WHITELIST_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.ACCOUNT_UPGRADE_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.ACCOUNT_TRANSFER_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.ASSET_CREATE_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.ASSET_UPDATE_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.ASSET_UPDATE_BITASSET_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.ASSET_UPDATE_FEED_PRODUCERS_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.ASSET_ISSUE_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.ASSET_RESERVE_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.ASSET_FUND_FEE_POOL_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.ASSET_SETTLE_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.ASSET_GLOBAL_SETTLE_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.ASSET_PUBLISH_FEED_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.WITNESS_CREATE_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.WITNESS_UPDATE_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.PROPOSAL_CREATE_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.PROPOSAL_UPDATE_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.PROPOSAL_DELETE_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.WITHDRAW_PERMISSION_CREATE_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.WITHDRAW_PERMISSION_UPDATE_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.WITHDRAW_PERMISSION_CLAIM_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.WITHDRAW_PERMISSION_DELETE_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.COMMITTEE_MEMBER_CREATE_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.COMMITTEE_MEMBER_UPDATE_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.COMMITTEE_MEMBER_UPDATE_GLOBAL_PARAMETERS_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.VESTING_BALANCE_CREATE_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.VESTING_BALANCE_WITHDRAW_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.WORKER_CREATE_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.CUSTOM_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.ASSERT_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.BALANCE_CLAIM_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.OVERRIDE_TRANSFER_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.TRANSFER_TO_BLIND_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.BLIND_TRANSFER_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.TRANSFER_FROM_BLIND_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.ASSET_SETTLE_CANCEL_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.ASSET_CLAIM_FEES_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
}
if (operation != null) operationList.add(operation);
operation = null;
}
return new Transaction(blockData, operationList);
}catch(Exception e){
System.out.println("Exception. Msg: "+e.getMessage());
for(StackTraceElement el : e.getStackTrace()){
System.out.println(el.getFileName()+"#"+el.getMethodName()+":"+el.getLineNumber());
}
}
return new Transaction(blockData, operationList);
}
}
}

View file

@ -1,329 +0,0 @@
package cy.agorise.graphenej;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.annotations.Expose;
import java.io.ByteArrayOutputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Type;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import cy.agorise.graphenej.interfaces.ByteSerializable;
import cy.agorise.graphenej.interfaces.JsonSerializable;
/**
* Class that represents a graphene user account.
*/
public class UserAccount extends GrapheneObject implements ByteSerializable, JsonSerializable {
public static final String PROXY_TO_SELF = "1.2.5";
public static final String KEY_MEMBERSHIP_EXPIRATION_DATE = "membership_expiration_date";
public static final String KEY_REGISTRAR = "registrar";
public static final String KEY_REFERRER = "referrer";
public static final String KEY_LIFETIME_REFERRER = "lifetime_referrer";
public static final String KEY_NETWORK_FEE_PERCENTAGE = "network_fee_percentage";
public static final String KEY_LIFETIME_REFERRER_FEE_PERCENTAGE = "lifetime_referrer_fee_percentage";
public static final String KEY_REFERRER_REWARD_PERCENTAGE = "referrer_rewards_percentage";
public static final String KEY_NAME = "name";
public static final String KEY_OWNER = "owner";
public static final String KEY_ACTIVE = "active";
public static final String KEY_OPTIONS = "options";
public static final String KEY_STATISTICS = "statistics";
public static final String KEY_WHITELISTING_ACCOUNTS = "whitelisting_accounts";
public static final String KEY_BLACKLISTING_ACCOUNTS = "blacklisting_accounts";
public static final String KEY_WHITELISTED_ACCOUNTS = "whitelisted_accounts";
public static final String KEY_BLACKLISTED_ACCOUNTS = "blacklisted_accounts";
public static final String KEY_OWNER_SPECIAL_AUTHORITY = "owner_special_authority";
public static final String KEY_ACTIVE_SPECIAL_AUTHORITY = "active_special_authority";
public static final String KEY_N_CONTROL_FLAGS = "top_n_control_flags";
@Expose
private String name;
@Expose
private Authority owner;
@Expose
private Authority active;
@Expose
private AccountOptions options;
@Expose
private String statistics;
@Expose
private long membershipExpirationDate;
@Expose
private String registrar;
@Expose
private String referrer;
@Expose
private String lifetimeReferrer;
@Expose
private long networkFeePercentage;
@Expose
private long lifetimeReferrerFeePercentage;
@Expose
private long referrerRewardsPercentage;
/**
* Constructor that expects a user account in the string representation.
* That is in the 1.2.x format.
* @param id: The string representing the user account.
*/
public UserAccount(String 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.name = name;
}
/**
* Getter for the account name field.
* @return: The name of this account.
*/
public String getName() {
return name;
}
/**
* Setter for the account name field.
* @param name: The account name.
*/
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
return this.getObjectId().equals(((UserAccount)o).getObjectId());
}
@Override
public int hashCode() {
return this.getObjectId().hashCode();
}
@Override
public byte[] toBytes() {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
DataOutput out = new DataOutputStream(byteArrayOutputStream);
try {
Varint.writeUnsignedVarLong(this.instance, out);
} catch (IOException e) {
e.printStackTrace();
}
return byteArrayOutputStream.toByteArray();
}
@Override
public String toJsonString() {
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
return gson.toJson(this);
}
@Override
public JsonObject toJsonObject() {
return null;
}
@Override
public String toString() {
return this.toJsonString();
}
public long getMembershipExpirationDate() {
return membershipExpirationDate;
}
public void setMembershipExpirationDate(long membershipExpirationDate) {
this.membershipExpirationDate = membershipExpirationDate;
}
public String getRegistrar() {
return registrar;
}
public void setRegistrar(String registrar) {
this.registrar = registrar;
}
public String getReferrer() {
return referrer;
}
public void setReferrer(String referrer) {
this.referrer = referrer;
}
public String getLifetimeReferrer() {
return lifetimeReferrer;
}
public void setLifetimeReferrer(String lifetimeReferrer) {
this.lifetimeReferrer = lifetimeReferrer;
}
public long getNetworkFeePercentage() {
return networkFeePercentage;
}
public void setNetworkFeePercentage(long networkFeePercentage) {
this.networkFeePercentage = networkFeePercentage;
}
public long getLifetimeReferrerFeePercentage() {
return lifetimeReferrerFeePercentage;
}
public void setLifetimeReferrerFeePercentage(long lifetimeReferrerFeePercentage) {
this.lifetimeReferrerFeePercentage = lifetimeReferrerFeePercentage;
}
public long getReferrerRewardsPercentage() {
return referrerRewardsPercentage;
}
public void setReferrerRewardsPercentage(long referrerRewardsPercentage) {
this.referrerRewardsPercentage = referrerRewardsPercentage;
}
public Authority getOwner() {
return owner;
}
public void setOwner(Authority owner) {
this.owner = owner;
}
public Authority getActive() {
return active;
}
public void setActive(Authority active) {
this.active = active;
}
public AccountOptions getOptions() {
return options;
}
public void setOptions(AccountOptions options) {
this.options = options;
}
public String getStatistics() {
return statistics;
}
public void setStatistics(String statistics) {
this.statistics = statistics;
}
/**
* Deserializer used to build a UserAccount instance from the full JSON-formatted response obtained
* by the 'get_objects' API call.
*/
public static class UserAccountFullDeserializer implements JsonDeserializer<UserAccount> {
@Override
public UserAccount deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonAccount = json.getAsJsonObject();
SimpleDateFormat dateFormat = new SimpleDateFormat(Util.TIME_DATE_FORMAT);
// Retrieving and deserializing fields
String id = jsonAccount.get(KEY_ID).getAsString();
String name = jsonAccount.get(KEY_NAME).getAsString();
UserAccount userAccount = new UserAccount(id, name);
AccountOptions options = context.deserialize(jsonAccount.get(KEY_OPTIONS), AccountOptions.class);
Authority owner = context.deserialize(jsonAccount.get(KEY_OWNER), Authority.class);
Authority active = context.deserialize(jsonAccount.get(KEY_ACTIVE), Authority.class);
// Setting deserialized fields into the created instance
userAccount.setRegistrar(jsonAccount.get(KEY_REGISTRAR).getAsString());
// Handling the deserialization and assignation of the membership date, which internally
// is stored as a long POSIX time value
try{
Date date = dateFormat.parse(jsonAccount.get(KEY_MEMBERSHIP_EXPIRATION_DATE).getAsString());
userAccount.setMembershipExpirationDate(date.getTime());
} catch (ParseException e) {
System.out.println("ParseException. Msg: "+e.getMessage());
}
// Setting the other fields
userAccount.setReferrer(jsonAccount.get(KEY_REFERRER).getAsString());
userAccount.setLifetimeReferrer(jsonAccount.get(KEY_LIFETIME_REFERRER).getAsString());
userAccount.setNetworkFeePercentage(jsonAccount.get(KEY_NETWORK_FEE_PERCENTAGE).getAsLong());
userAccount.setLifetimeReferrerFeePercentage(jsonAccount.get(KEY_LIFETIME_REFERRER_FEE_PERCENTAGE).getAsLong());
userAccount.setReferrerRewardsPercentage(jsonAccount.get(KEY_REFERRER_REWARD_PERCENTAGE).getAsLong());
userAccount.setOwner(owner);
userAccount.setActive(active);
userAccount.setOptions(options);
userAccount.setStatistics(jsonAccount.get(KEY_STATISTICS).getAsString());
return userAccount;
}
}
/**
* 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);
}
}
/**
* Custom deserializer used to deserialize user accounts as provided by the response of the 'get_key_references' api call.
* This response contains serialized user accounts in the form [["id1","id2"]]
*/
public static class UserAccountSimpleDeserializer implements JsonDeserializer<UserAccount> {
@Override
public UserAccount deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
String id = json.getAsString();
return new UserAccount(id);
}
}
}

View file

@ -1,347 +0,0 @@
package cy.agorise.graphenej;
import com.google.common.primitives.Bytes;
import org.tukaani.xz.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.spongycastle.crypto.DataLengthException;
import org.spongycastle.crypto.InvalidCipherTextException;
import org.spongycastle.crypto.engines.AESFastEngine;
import org.spongycastle.crypto.modes.CBCBlockCipher;
import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.spongycastle.crypto.params.KeyParameter;
import org.spongycastle.crypto.params.ParametersWithIV;
/**
* Class used to encapsulate common utility methods
*/
public class Util {
public static final String TAG = "Util";
private static final char[] hexArray = "0123456789abcdef".toCharArray();
public static final int LZMA = 0;
public static final int XZ = 1;
/**
* AES encryption key length in bytes
*/
public static final int KEY_LENGTH = 32;
/**
* Time format used across the platform
*/
public static final String TIME_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss";
/**
* Converts an hexadecimal string to its corresponding byte[] value.
* @param s: String with hexadecimal numbers representing a byte array.
* @return: The actual byte array.
*/
public static byte[] hexToBytes(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
/**
* Converts a byte array, into a user-friendly hexadecimal string.
* @param bytes: A byte array.
* @return: A string with the representation of the byte array.
*/
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for ( int j = 0; j < bytes.length; j++ ) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
/**
* Decodes an ascii string to a byte array.
* @param data: Arbitrary ascii-encoded string.
* @return: Array of bytes.
*/
public static byte[] hexlify(String data){
ByteBuffer buffer = ByteBuffer.allocate(data.length());
for(char letter : data.toCharArray()){
buffer.put((byte) letter);
}
return buffer.array();
}
/**
* Utility function that compresses data using the LZMA algorithm.
* @param inputBytes Input bytes of the data to be compressed.
* @param which Which subclass of the FinishableOutputStream to use.
* @return Compressed data
* @author Henry Varona
*/
public static byte[] compress(byte[] inputBytes, int which) {
FinishableOutputStream out = null;
try {
ByteArrayInputStream input = new ByteArrayInputStream(inputBytes);
ByteArrayOutputStream output = new ByteArrayOutputStream(2048);
LZMA2Options options = new LZMA2Options();
if(which == Util.LZMA) {
out = new LZMAOutputStream(output, options, -1);
}else if(which == Util.XZ){
out = new XZOutputStream(output, options);
}
byte[] inputBuffer = new byte[inputBytes.length];
int size;
while ((size = input.read(inputBuffer)) != -1) {
out.write(inputBuffer, 0, size);
}
out.finish();
return output.toByteArray();
} catch (IOException ex) {
Logger.getLogger(Util.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
out.close();
} catch (IOException ex) {
Logger.getLogger(Util.class.getName()).log(Level.SEVERE, null, ex);
}
}
return null;
}
/**
* Utility function that decompresses data that has been compressed using the LZMA algorithm
* by the {@link Util#compress(byte[], int)} method.
* @param inputBytes Compressed data.
* @param which Which subclass if InputStream to use.
* @return Uncompressed data
* @author Henry Varona
*/
public static byte[] decompress(byte[] inputBytes, int which) {
InputStream in = null;
try {
System.out.println("Bytes: "+Util.bytesToHex(inputBytes));
ByteArrayInputStream input = new ByteArrayInputStream(inputBytes);
ByteArrayOutputStream output = new ByteArrayOutputStream(16*2048);
if(which == XZ) {
in = new XZInputStream(input);
}else if(which == LZMA){
in = new LZMAInputStream(input);
}
int size;
try{
while ((size = in.read()) != -1) {
output.write(size);
}
}catch(CorruptedInputException e){
// Taking property byte
byte[] properties = Arrays.copyOfRange(inputBytes, 0, 1);
// Taking dict size bytes
byte[] dictSize = Arrays.copyOfRange(inputBytes, 1, 5);
// Taking uncompressed size bytes
byte[] uncompressedSize = Arrays.copyOfRange(inputBytes, 5, 13);
// Reversing bytes in header
byte[] header = Bytes.concat(properties, Util.revertBytes(dictSize), Util.revertBytes(uncompressedSize));
byte[] payload = Arrays.copyOfRange(inputBytes, 13, inputBytes.length);
// Trying again
input = new ByteArrayInputStream(Bytes.concat(header, payload));
output = new ByteArrayOutputStream(2048);
if(which == XZ) {
in = new XZInputStream(input);
}else if(which == LZMA){
in = new LZMAInputStream(input);
}
try{
while ((size = in.read()) != -1) {
output.write(size);
}
}catch(CorruptedInputException ex){
System.out.println("CorruptedInputException. Msg: "+ex.getMessage());
}
}
in.close();
return output.toByteArray();
} catch (IOException ex) {
Logger.getLogger(Util.class.getName()).log(Level.SEVERE, null, ex);
}
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();
}
public static byte[] revertBytes(byte[] array){
byte[] reverted = new byte[array.length];
for(int i = 0; i < reverted.length; i++){
reverted[i] = array[array.length - i - 1];
}
return reverted;
}
/**
* Function to encrypt a message with AES
* @param input data to encrypt
* @param key key for encryption
* @return AES Encription of input
*/
public static byte[] encryptAES(byte[] input, byte[] key) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-512");
byte[] result = md.digest(key);
byte[] ivBytes = new byte[16];
System.arraycopy(result, 32, ivBytes, 0, 16);
byte[] sksBytes = new byte[32];
System.arraycopy(result, 0, sksBytes, 0, 32);
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESFastEngine()));
cipher.init(true, new ParametersWithIV(new KeyParameter(sksBytes), ivBytes));
byte[] temp = new byte[input.length + (16 - (input.length % 16))];
System.arraycopy(input, 0, temp, 0, input.length);
Arrays.fill(temp, input.length, temp.length, (byte) (16 - (input.length % 16)));
byte[] out = new byte[cipher.getOutputSize(temp.length)];
int proc = cipher.processBytes(temp, 0, temp.length, out, 0);
cipher.doFinal(out, proc);
temp = new byte[out.length - 16];
System.arraycopy(out, 0, temp, 0, temp.length);
return temp;
} catch (NoSuchAlgorithmException | DataLengthException | IllegalStateException | InvalidCipherTextException ex) {
}
return null;
}
/**
* Function to decrypt a message with AES encryption
* @param input data to decrypt
* @param key key for decryption
* @return input decrypted with AES. Null if the decrypt failed (Bad Key)
*/
public static byte[] decryptAES(byte[] input, byte[] key) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-512");
byte[] result = md.digest(key);
byte[] ivBytes = new byte[16];
System.arraycopy(result, 32, ivBytes, 0, 16);
byte[] sksBytes = new byte[32];
System.arraycopy(result, 0, sksBytes, 0, 32);
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESFastEngine()));
cipher.init(false, new ParametersWithIV(new KeyParameter(sksBytes), ivBytes));
byte[] pre_out = new byte[cipher.getOutputSize(input.length)];
int proc = cipher.processBytes(input, 0, input.length, pre_out, 0);
int proc2 = cipher.doFinal(pre_out, proc);
byte[] out = new byte[proc+proc2];
System.arraycopy(pre_out, 0, out, 0, proc+proc2);
//Unpadding
byte countByte = (byte)((byte)out[out.length-1] % 16);
int count = countByte & 0xFF;
if ((count > 15) || (count <= 0)){
return out;
}
byte[] temp = new byte[count];
System.arraycopy(out, out.length - count, temp, 0, temp.length);
byte[] temp2 = new byte[count];
Arrays.fill(temp2, (byte) count);
if (Arrays.equals(temp, temp2)) {
temp = new byte[out.length - count];
System.arraycopy(out, 0, temp, 0, out.length - count);
return temp;
} else {
return out;
}
} catch (NoSuchAlgorithmException | DataLengthException | IllegalStateException | InvalidCipherTextException ex) {
ex.printStackTrace();
}
return null;
}
/**
* Transform an array of bytes to an hex String representation
* @param input array of bytes to transform as a string
* @return Input as a String
*/
public static String byteToString(byte[] input) {
StringBuilder result = new StringBuilder();
for (byte in : input) {
if ((in & 0xff) < 0x10) {
result.append("0");
}
result.append(Integer.toHexString(in & 0xff));
}
return result.toString();
}
/**
* Converts a base value to its standard version, considering the precision of the asset.
*
* By standard representation we mean here the value that is usually presented to the user,
* and which already takes into account the precision of the asset.
*
* For example, a base representation of the core token BTS would be 260000. By taking into
* consideration the precision, the same value when converted to the standard format will
* be 2.6 BTS.
*
* @param assetAmount: The asset amount instance.
* @return: Converts from base to standard representation.
*/
public static double fromBase(AssetAmount assetAmount){
long value = assetAmount.getAmount().longValue();
int precision = assetAmount.getAsset().getPrecision();
if(precision != 0)
return value / Math.pow(10, precision);
else
return 0;
}
/**
* Converts a value and its corresponding precision to a base value.
* @param value: The value in the standard format
* @param precision: The precision of the asset.
* @return: A value in its base representation.
*/
public static long toBase(double value, int precision){
return (long) (value * Math.pow(10, precision));
}
}

View file

@ -1,224 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cy.agorise.graphenej;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
/**
* <p>Encodes signed and unsigned values using a common variable-length
* scheme, found for example in
* <a href="http://code.google.com/apis/protocolbuffers/docs/encoding.html">
* Google's Protocol Buffers</a>. It uses fewer bytes to encode smaller values,
* but will use slightly more bytes to encode large values.</p>
* <p/>
* <p>Signed values are further encoded using so-called zig-zag encoding
* in order to make them "compatible" with variable-length encoding.</p>
*/
public final class Varint {
private Varint() {
}
/**
* Encodes a value using the variable-length encoding from
* <a href="http://code.google.com/apis/protocolbuffers/docs/encoding.html">
* Google Protocol Buffers</a>. It uses zig-zag encoding to efficiently
* encode signed values. If values are known to be nonnegative,
* {@link #writeUnsignedVarLong(long, DataOutput)} should be used.
*
* @param value value to encode
* @param out to write bytes to
* @throws IOException if {@link DataOutput} throws {@link IOException}
*/
public static void writeSignedVarLong(long value, DataOutput out) throws IOException {
// Great trick from http://code.google.com/apis/protocolbuffers/docs/encoding.html#types
writeUnsignedVarLong((value << 1) ^ (value >> 63), out);
}
/**
* Encodes a value using the variable-length encoding from
* <a href="http://code.google.com/apis/protocolbuffers/docs/encoding.html">
* Google Protocol Buffers</a>. Zig-zag is not used, so input must not be negative.
* If values can be negative, use {@link #writeSignedVarLong(long, DataOutput)}
* instead. This method treats negative input as like a large unsigned value.
*
* @param value value to encode
* @param out to write bytes to
* @throws IOException if {@link DataOutput} throws {@link IOException}
*/
public static void writeUnsignedVarLong(long value, DataOutput out) throws IOException {
while ((value & 0xFFFFFFFFFFFFFF80L) != 0L) {
out.writeByte(((int) value & 0x7F) | 0x80);
value >>>= 7;
}
out.writeByte((int) value & 0x7F);
}
/**
* @see #writeSignedVarLong(long, DataOutput)
*/
public static void writeSignedVarInt(int value, DataOutput out) throws IOException {
// Great trick from http://code.google.com/apis/protocolbuffers/docs/encoding.html#types
writeUnsignedVarInt((value << 1) ^ (value >> 31), out);
}
/**
* @see #writeUnsignedVarLong(long, DataOutput)
*/
public static void writeUnsignedVarInt(int value, DataOutput out) throws IOException {
while ((value & 0xFFFFFF80) != 0L) {
out.writeByte((value & 0x7F) | 0x80);
value >>>= 7;
}
out.writeByte(value & 0x7F);
}
public static byte[] writeSignedVarInt(int value) {
// Great trick from http://code.google.com/apis/protocolbuffers/docs/encoding.html#types
return writeUnsignedVarInt((value << 1) ^ (value >> 31));
}
/**
* @see #writeUnsignedVarLong(long, DataOutput)
* <p/>
* This one does not use streams and is much faster.
* Makes a single object each time, and that object is a primitive array.
*/
public static byte[] writeUnsignedVarInt(int value) {
byte[] byteArrayList = new byte[10];
int i = 0;
while ((value & 0xFFFFFF80) != 0L) {
byteArrayList[i++] = ((byte) ((value & 0x7F) | 0x80));
value >>>= 7;
}
byteArrayList[i] = ((byte) (value & 0x7F));
byte[] out = new byte[i + 1];
for (; i >= 0; i--) {
out[i] = byteArrayList[i];
}
return out;
}
/**
* @param in to read bytes from
* @return decode value
* @throws IOException if {@link DataInput} throws {@link IOException}
* @throws IllegalArgumentException if variable-length value does not terminate
* after 9 bytes have been read
* @see #writeSignedVarLong(long, DataOutput)
*/
public static long readSignedVarLong(DataInput in) throws IOException {
long raw = readUnsignedVarLong(in);
// This undoes the trick in writeSignedVarLong()
long temp = (((raw << 63) >> 63) ^ raw) >> 1;
// This extra step lets us deal with the largest signed values by treating
// negative results from read unsigned methods as like unsigned values
// Must re-flip the top bit if the original read value had it set.
return temp ^ (raw & (1L << 63));
}
/**
* @param in to read bytes from
* @return decode value
* @throws IOException if {@link DataInput} throws {@link IOException}
* @throws IllegalArgumentException if variable-length value does not terminate
* after 9 bytes have been read
* @see #writeUnsignedVarLong(long, DataOutput)
*/
public static long readUnsignedVarLong(DataInput in) throws IOException {
long value = 0L;
int i = 0;
long b;
while (((b = in.readByte()) & 0x80L) != 0) {
value |= (b & 0x7F) << i;
i += 7;
if (i > 63) {
throw new IllegalArgumentException("Variable length quantity is too long");
}
}
return value | (b << i);
}
/**
* @throws IllegalArgumentException if variable-length value does not terminate
* after 5 bytes have been read
* @throws IOException if {@link DataInput} throws {@link IOException}
* @see #readSignedVarLong(DataInput)
*/
public static int readSignedVarInt(DataInput in) throws IOException {
int raw = readUnsignedVarInt(in);
// This undoes the trick in writeSignedVarInt()
int temp = (((raw << 31) >> 31) ^ raw) >> 1;
// This extra step lets us deal with the largest signed values by treating
// negative results from read unsigned methods as like unsigned values.
// Must re-flip the top bit if the original read value had it set.
return temp ^ (raw & (1 << 31));
}
/**
* @throws IllegalArgumentException if variable-length value does not terminate
* after 5 bytes have been read
* @throws IOException if {@link DataInput} throws {@link IOException}
* @see #readUnsignedVarLong(DataInput)
*/
public static int readUnsignedVarInt(DataInput in) throws IOException {
int value = 0;
int i = 0;
int b;
while (((b = in.readByte()) & 0x80) != 0) {
value |= (b & 0x7F) << i;
i += 7;
if (i > 35) {
throw new IllegalArgumentException("Variable length quantity is too long");
}
}
return value | (b << i);
}
public static int readSignedVarInt(byte[] bytes) {
int raw = readUnsignedVarInt(bytes);
// This undoes the trick in writeSignedVarInt()
int temp = (((raw << 31) >> 31) ^ raw) >> 1;
// This extra step lets us deal with the largest signed values by treating
// negative results from read unsigned methods as like unsigned values.
// Must re-flip the top bit if the original read value had it set.
return temp ^ (raw & (1 << 31));
}
public static int readUnsignedVarInt(byte[] bytes) {
int value = 0;
int i = 0;
byte rb = Byte.MIN_VALUE;
for (byte b : bytes) {
rb = b;
if ((b & 0x80) == 0) {
break;
}
value |= (b & 0x7f) << i;
i += 7;
if (i > 35) {
throw new IllegalArgumentException("Variable length quantity is too long");
}
}
return value | (rb << i);
}
}

View file

@ -1,33 +0,0 @@
package cy.agorise.graphenej;
import cy.agorise.graphenej.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 };
}
}

View file

@ -1,84 +0,0 @@
package cy.agorise.graphenej.api;
import com.neovisionaries.ws.client.WebSocket;
import com.neovisionaries.ws.client.WebSocketAdapter;
import com.neovisionaries.ws.client.WebSocketException;
import org.w3c.dom.Node;
import cy.agorise.graphenej.interfaces.NodeErrorListener;
import cy.agorise.graphenej.interfaces.WitnessResponseListener;
import cy.agorise.graphenej.models.BaseResponse;
/**
* Base class that should be extended by any implementation of a specific request to the full node.
*/
public abstract class BaseGrapheneHandler extends WebSocketAdapter {
protected WitnessResponseListener mListener;
protected NodeErrorListener mErrorListener;
/**
* The 'id' field of a message to the node. This is used in order to multiplex different messages
* using the same websocket connection.
*
* For example:
*
* {"id":5,"method":"call","params":[0,"get_accounts",[["1.2.100"]]],"jsonrpc":"2.0"}
*
* The 'requestId' here is 5.
*/
protected long requestId;
/**
* Constructor (The original constructor, should be replaced with the one that receives
* NodeErrorListener instead of WitnessResponseListener)
*
* @param listener listener to be notified in if an error occurs
*/
@Deprecated
public BaseGrapheneHandler(WitnessResponseListener listener){
this.mListener = listener;
}
/**
* Constructor
*
* @param listener listener to be notified if an error occurs
*/
public BaseGrapheneHandler(NodeErrorListener listener){
this.mErrorListener = listener;
}
@Override
public void onError(WebSocket websocket, WebSocketException cause) throws Exception {
System.out.println("onError. cause: "+cause.getMessage());
mErrorListener.onError(new BaseResponse.Error(cause.getMessage()));
websocket.disconnect();
}
@Override
public void handleCallbackError(WebSocket websocket, Throwable cause) throws Exception {
System.out.println("handleCallbackError. message: "+cause.getMessage()+", error: "+cause.getClass());
for (StackTraceElement element : cause.getStackTrace()){
System.out.println(element.getFileName()+"#"+element.getClassName()+":"+element.getLineNumber());
}
// Should be replaced for mErrorListener (NodeErrorListener type) only in the future
if(mErrorListener != null){
mErrorListener.onError(new BaseResponse.Error(cause.getMessage()));
}
else{
mListener.onError(new BaseResponse.Error(cause.getMessage()));
}
websocket.disconnect();
}
public void setRequestId(long id){
this.requestId = id;
}
public long getRequestId(){
return this.requestId;
}
}

View file

@ -1,106 +0,0 @@
package cy.agorise.graphenej.api;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.neovisionaries.ws.client.WebSocket;
import com.neovisionaries.ws.client.WebSocketFrame;
import cy.agorise.graphenej.Asset;
import cy.agorise.graphenej.AssetAmount;
import cy.agorise.graphenej.RPC;
import cy.agorise.graphenej.UserAccount;
import cy.agorise.graphenej.interfaces.WitnessResponseListener;
import cy.agorise.graphenej.models.ApiCall;
import cy.agorise.graphenej.models.WitnessResponse;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Class that implements get_account_balances request handler.
*
* Get an accounts balances in various assets.
*
* The response returns the balances of the account
*
* @see <a href="https://goo.gl/faFdey">get_account_balances API doc</a>
*
*/
public class GetAccountBalances extends BaseGrapheneHandler {
private UserAccount mUserAccount;
private List<Asset> mAssetList;
private boolean mOneTime;
/**
* Default Constructor
*
* @param userAccount account to get balances for
* @param assets list of the assets to get balances of; if empty, get all assets account
* has a balance in
* @param oneTime boolean value indicating if WebSocket must be closed (true) or not
* (false) after the response
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public GetAccountBalances(UserAccount userAccount, List<Asset> assets, boolean oneTime, WitnessResponseListener listener) {
super(listener);
this.mUserAccount = userAccount;
this.mAssetList = assets;
this.mOneTime = oneTime;
}
/**
* Using this constructor the WebSocket connection closes after the response.
*
* @param userAccount account to get balances for
* @param assets list of the assets to get balances of; if empty, get all assets account
* has a balance in
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public GetAccountBalances(UserAccount userAccount, List<Asset> assets, WitnessResponseListener listener) {
this(userAccount, assets, true, listener);
}
@Override
public void onConnected(WebSocket websocket, Map<String, List<String>> headers) throws Exception {
ArrayList<Serializable> params = new ArrayList<>();
ArrayList<Serializable> assetList = new ArrayList<>();
for(Asset asset : mAssetList){
assetList.add(asset.getObjectId());
}
params.add(mUserAccount.getObjectId());
params.add(assetList);
ApiCall apiCall = new ApiCall(0, RPC.GET_ACCOUNT_BALANCES, params, RPC.VERSION, 0);
websocket.sendText(apiCall.toJsonString());
}
@Override
public void onTextFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
if(frame.isTextFrame()){
System.out.println("<< "+frame.getPayloadText());
}
String response = frame.getPayloadText();
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer());
Type WitnessResponseType = new TypeToken<WitnessResponse<List<AssetAmount>>>(){}.getType();
WitnessResponse<List<AssetAmount>> witnessResponse = gsonBuilder.create().fromJson(response, WitnessResponseType);
mListener.onSuccess(witnessResponse);
if(mOneTime){
websocket.disconnect();
}
}
@Override
public void onFrameSent(WebSocket websocket, WebSocketFrame frame) throws Exception {
if(frame.isTextFrame()){
System.out.println(">> "+frame.getPayloadText());
}
}
}

View file

@ -1,102 +0,0 @@
package cy.agorise.graphenej.api;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.neovisionaries.ws.client.WebSocket;
import com.neovisionaries.ws.client.WebSocketFrame;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import cy.agorise.graphenej.AccountOptions;
import cy.agorise.graphenej.Authority;
import cy.agorise.graphenej.RPC;
import cy.agorise.graphenej.interfaces.WitnessResponseListener;
import cy.agorise.graphenej.models.AccountProperties;
import cy.agorise.graphenej.models.ApiCall;
import cy.agorise.graphenej.models.WitnessResponse;
/**
* Class that implements get_account_by_name request handler.
*
* Get an accounts info by name.
*
* The response returns account data that refer to the name.
*
* @see <a href="https://goo.gl/w75qjV">get_account_by_name API doc</a>
*/
public class GetAccountByName extends BaseGrapheneHandler {
private String accountName;
private WitnessResponseListener mListener;
private boolean mOneTime;
/**
* Default Constructor
*
* @param accountName name of the account to get info
* @param oneTime boolean value indicating if WebSocket must be closed (true) or not
* (false) after the response
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public GetAccountByName(String accountName, boolean oneTime, WitnessResponseListener listener){
super(listener);
this.accountName = accountName;
this.mOneTime = oneTime;
this.mListener = listener;
}
/**
* Using this constructor the WebSocket connection closes after the response.
*
* @param accountName name of the account to get info
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public GetAccountByName(String accountName, WitnessResponseListener listener){
this(accountName, true, listener);
}
@Override
public void onConnected(WebSocket websocket, Map<String, List<String>> headers) throws Exception {
ArrayList<Serializable> accountParams = new ArrayList<>();
accountParams.add(this.accountName);
ApiCall getAccountByName = new ApiCall(0, RPC.CALL_GET_ACCOUNT_BY_NAME, accountParams, RPC.VERSION, 1);
websocket.sendText(getAccountByName.toJsonString());
}
@Override
public void onTextFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
if(frame.isTextFrame())
System.out.println("<<< "+frame.getPayloadText());
String response = frame.getPayloadText();
GsonBuilder builder = new GsonBuilder();
Type GetAccountByNameResponse = new TypeToken<WitnessResponse<AccountProperties>>(){}.getType();
builder.registerTypeAdapter(Authority.class, new Authority.AuthorityDeserializer());
builder.registerTypeAdapter(AccountOptions.class, new AccountOptions.AccountOptionsDeserializer());
WitnessResponse<AccountProperties> witnessResponse = builder.create().fromJson(response, GetAccountByNameResponse);
if(witnessResponse.error != null){
this.mListener.onError(witnessResponse.error);
}else{
this.mListener.onSuccess(witnessResponse);
}
if(mOneTime){
websocket.disconnect();
}
}
@Override
public void onFrameSent(WebSocket websocket, WebSocketFrame frame) throws Exception {
if(frame.isTextFrame()){
System.out.println(">>> "+frame.getPayloadText());
}
}
}

View file

@ -1,8 +0,0 @@
package cy.agorise.graphenej.api;
/**
* Created by nelson on 12/26/16.
*/
//TODO: Implement if needed: http://docs.bitshares.eu/api/history.html?highlight=get_market_history#account-history-api
public class GetAccountHistory {
}

View file

@ -1,139 +0,0 @@
package cy.agorise.graphenej.api;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.neovisionaries.ws.client.WebSocket;
import com.neovisionaries.ws.client.WebSocketFrame;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import cy.agorise.graphenej.AccountOptions;
import cy.agorise.graphenej.Authority;
import cy.agorise.graphenej.RPC;
import cy.agorise.graphenej.UserAccount;
import cy.agorise.graphenej.interfaces.WitnessResponseListener;
import cy.agorise.graphenej.models.AccountProperties;
import cy.agorise.graphenej.models.ApiCall;
import cy.agorise.graphenej.models.WitnessResponse;
/**
* Class that implements get_accounts request handler.
*
* Get a list of accounts by ID.
*
* The response returns the accounts corresponding to the provided IDs.
*
* @see <a href="https://goo.gl/r5RqKG">get_accounts API doc</a>
*/
public class GetAccounts extends BaseGrapheneHandler {
private String accountId;
private List<UserAccount> userAccounts;
private WitnessResponseListener mListener;
private boolean mOneTime;
/**
* Constructor for one account only.
*
* @param accountId ID of the account to retrieve
* @param oneTime boolean value indicating if WebSocket must be closed (true) or not
* (false) after the response
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public GetAccounts(String accountId, boolean oneTime, WitnessResponseListener listener){
super(listener);
this.accountId = accountId;
this.mOneTime = oneTime;
this.mListener = listener;
}
/**
* Constructor for account list.
*
* @param accounts list with the accounts to retrieve
* @param oneTime boolean value indicating if WebSocket must be closed (true) or not
* (false) after the response
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public GetAccounts(List<UserAccount> accounts, boolean oneTime, WitnessResponseListener listener){
super(listener);
this.userAccounts = accounts;
this.mOneTime = oneTime;
this.mListener = listener;
}
/**
* Using this constructor the WebSocket connection closes after the response. (Account based)
*
* @param accountId ID of the account to retrieve
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public GetAccounts(String accountId, WitnessResponseListener listener){
this(accountId, true, listener);
}
/**
* Using this constructor the WebSocket connection closes after the response. (Account List
* based)
*
* @param accounts list with the accounts to retrieve
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public GetAccounts(List<UserAccount> accounts, WitnessResponseListener listener){
this(accounts, true, listener);
}
@Override
public void onConnected(WebSocket websocket, Map<String, List<String>> headers) throws Exception {
ArrayList<Serializable> params = new ArrayList();
ArrayList<Serializable> accountIds = new ArrayList();
if(accountId == null){
for(UserAccount account : userAccounts) {
accountIds.add(account.getObjectId());
}
}else{
accountIds.add(accountId);
}
params.add(accountIds);
ApiCall getAccountByAddress = new ApiCall(0, RPC.CALL_GET_ACCOUNTS, params, RPC.VERSION, (int) requestId);
websocket.sendText(getAccountByAddress.toJsonString());
}
@Override
public void onTextFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
System.out.println("<<< "+frame.getPayloadText());
String response = frame.getPayloadText();
GsonBuilder builder = new GsonBuilder();
Type GetAccountByAddressResponse = new TypeToken<WitnessResponse<List<AccountProperties>>>() {}.getType();
builder.registerTypeAdapter(Authority.class, new Authority.AuthorityDeserializer());
builder.registerTypeAdapter(AccountOptions.class, new AccountOptions.AccountOptionsDeserializer());
WitnessResponse<List<AccountProperties>> witnessResponse = builder.create().fromJson(response, GetAccountByAddressResponse);
if (witnessResponse.error != null) {
this.mListener.onError(witnessResponse.error);
} else {
this.mListener.onSuccess(witnessResponse);
}
if(mOneTime){
websocket.disconnect();
}
}
@Override
public void onFrameSent(WebSocket websocket, WebSocketFrame frame) throws Exception {
if(frame.isTextFrame())
System.out.println(">>> "+frame.getPayloadText());
}
}

View file

@ -1,115 +0,0 @@
package cy.agorise.graphenej.api;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.neovisionaries.ws.client.WebSocket;
import com.neovisionaries.ws.client.WebSocketFrame;
import cy.agorise.graphenej.RPC;
import cy.agorise.graphenej.interfaces.WitnessResponseListener;
import cy.agorise.graphenej.models.*;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Class that implements get_all_asset_holders request handler.
*
* Get a list of all system assets with holders count.
*
* The response returns the list of all assets with holders count.
*
* @see <a href="https://goo.gl/AgTSLU">get_all_asset_holders API doc (source code ref.)</a>
*/
public class GetAllAssetHolders extends BaseGrapheneHandler {
private final static int LOGIN_ID = 1;
private final static int GET_ASSET_API_ID = 2;
private final static int GET_ALL_ASSET_HOLDERS_COUNT = 3;
private int currentId = 1;
private int assetApiId = -1;
private boolean mOneTime;
/**
* Default Constructor
*
* @param oneTime boolean value indicating if WebSocket must be closed (true) or not
* (false) after the response
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public GetAllAssetHolders(boolean oneTime, WitnessResponseListener listener) {
super(listener);
this.mOneTime = oneTime;
}
/**
* Using this constructor the WebSocket connection closes after the response.
*
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the success/failure
* of the transaction broadcast operation.
*/
public GetAllAssetHolders(WitnessResponseListener listener) { this(true, listener);}
@Override
public void onConnected(WebSocket websocket, Map<String, List<String>> headers) throws Exception {
ArrayList<Serializable> loginParams = new ArrayList<>();
loginParams.add(null);
loginParams.add(null);
ApiCall loginCall = new ApiCall(1, RPC.CALL_LOGIN, loginParams, RPC.VERSION, currentId);
websocket.sendText(loginCall.toJsonString());
}
@Override
public void onTextFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
if(frame.isTextFrame())
System.out.println("<<< "+frame.getPayloadText());
String response = frame.getPayloadText();
Gson gson = new Gson();
BaseResponse baseResponse = gson.fromJson(response, BaseResponse.class);
if(baseResponse.error != null){
mListener.onError(baseResponse.error);
if(mOneTime){
websocket.disconnect();
}
}else {
currentId++;
ArrayList<Serializable> emptyParams = new ArrayList<>();
if (baseResponse.id == LOGIN_ID) {
ApiCall networkApiIdCall = new ApiCall(1, RPC.CALL_ASSET, emptyParams, RPC.VERSION, currentId);
websocket.sendText(networkApiIdCall.toJsonString());
}else if(baseResponse.id == GET_ASSET_API_ID){
Type ApiIdResponse = new TypeToken<WitnessResponse<Integer>>() {}.getType();
WitnessResponse<Integer> witnessResponse = gson.fromJson(response, ApiIdResponse);
assetApiId = witnessResponse.result;
ApiCall apiCall = new ApiCall(assetApiId, RPC.CALL_GET_ALL_ASSET_HOLDERS, emptyParams, RPC.VERSION, currentId);
websocket.sendText(apiCall.toJsonString());
} else if (baseResponse.id == GET_ALL_ASSET_HOLDERS_COUNT) {
Type AssetTokenHolders = new TypeToken<WitnessResponse<List<AssetHolderCount>>>(){}.getType();
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(AssetHolderCount.class, new AssetHolderCount.HoldersCountDeserializer());
WitnessResponse<List<AssetHolderCount>> witnessResponse = builder.create().fromJson(response, AssetTokenHolders);
mListener.onSuccess(witnessResponse);
if(mOneTime){
websocket.disconnect();
}
}else{
System.out.println("current id: "+currentId);
}
}
}
@Override
public void onFrameSent(WebSocket websocket, WebSocketFrame frame) throws Exception {
if(frame.isTextFrame()){
System.out.println(">>> "+frame.getPayloadText());
}
}
}

View file

@ -1,129 +0,0 @@
package cy.agorise.graphenej.api;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.neovisionaries.ws.client.WebSocket;
import com.neovisionaries.ws.client.WebSocketFrame;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import cy.agorise.graphenej.RPC;
import cy.agorise.graphenej.interfaces.WitnessResponseListener;
import cy.agorise.graphenej.models.ApiCall;
import cy.agorise.graphenej.models.BaseResponse;
import cy.agorise.graphenej.models.BlockHeader;
import cy.agorise.graphenej.models.WitnessResponse;
/**
* Class that implements get_block_header request handler.
*
* Retrieve a block header.
*
* The request returns the header of the referenced block, or null if no matching block was found
*
* @see <a href="https://goo.gl/qw1eeb">get_block_header API doc</a>
*/
public class GetBlockHeader extends BaseGrapheneHandler {
// Sequence of message ids
private final static int LOGIN_ID = 1;
private final static int GET_DATABASE_ID = 2;
private final static int GET_BLOCK_HEADER_ID = 3;
private long blockNumber;
private WitnessResponseListener mListener;
private int currentId = LOGIN_ID;
private int apiId = -1;
private boolean mOneTime;
/**
* Default Constructor
*
* @param blockNumber height of the block whose header should be returned
* @param oneTime boolean value indicating if WebSocket must be closed (true) or not
* (false) after the response
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public GetBlockHeader(long blockNumber, boolean oneTime, WitnessResponseListener listener){
super(listener);
this.blockNumber = blockNumber;
this.mOneTime = oneTime;
this.mListener = listener;
}
/**
* Using this constructor the WebSocket connection closes after the response.
*
* @param blockNumber height of the block whose header should be returned
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public GetBlockHeader(long blockNumber, WitnessResponseListener listener){
this(blockNumber, true, listener);
}
@Override
public void onConnected(WebSocket websocket, Map<String, List<String>> headers) throws Exception {
ArrayList<Serializable> loginParams = new ArrayList<>();
loginParams.add(null);
loginParams.add(null);
ApiCall loginCall = new ApiCall(1, RPC.CALL_LOGIN, loginParams, RPC.VERSION, currentId);
websocket.sendText(loginCall.toJsonString());
}
@Override
public void onTextFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
String response = frame.getPayloadText();
System.out.println("<<< "+response);
Gson gson = new Gson();
BaseResponse baseResponse = gson.fromJson(response, BaseResponse.class);
if(baseResponse.error != null){
mListener.onError(baseResponse.error);
if(mOneTime){
websocket.disconnect();
}
}else {
currentId++;
ArrayList<Serializable> emptyParams = new ArrayList<>();
if(baseResponse.id == LOGIN_ID){
ApiCall getDatabaseId = new ApiCall(1, RPC.CALL_DATABASE, emptyParams, RPC.VERSION, currentId);
websocket.sendText(getDatabaseId.toJsonString());
}else if(baseResponse.id == GET_DATABASE_ID){
Type ApiIdResponse = new TypeToken<WitnessResponse<Integer>>() {}.getType();
WitnessResponse<Integer> witnessResponse = gson.fromJson(response, ApiIdResponse);
apiId = witnessResponse.result.intValue();
ArrayList<Serializable> params = new ArrayList<>();
String blockNum = String.format("%d", this.blockNumber);
params.add(blockNum);
ApiCall loginCall = new ApiCall(apiId, RPC.CALL_GET_BLOCK_HEADER, params, RPC.VERSION, currentId);
websocket.sendText(loginCall.toJsonString());
}else if(baseResponse.id == GET_BLOCK_HEADER_ID){
Type RelativeAccountHistoryResponse = new TypeToken<WitnessResponse<BlockHeader>>(){}.getType();
WitnessResponse<BlockHeader> transfersResponse = gson.fromJson(response, RelativeAccountHistoryResponse);
mListener.onSuccess(transfersResponse);
if(mOneTime){
websocket.disconnect();
}
}
}
}
@Override
public void onFrameSent(WebSocket websocket, WebSocketFrame frame) throws Exception {
if(frame.isTextFrame())
System.out.println(">>> "+frame.getPayloadText());
}
}

View file

@ -1,129 +0,0 @@
package cy.agorise.graphenej.api;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.neovisionaries.ws.client.WebSocket;
import com.neovisionaries.ws.client.WebSocketFrame;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import cy.agorise.graphenej.Address;
import cy.agorise.graphenej.RPC;
import cy.agorise.graphenej.UserAccount;
import cy.agorise.graphenej.interfaces.WitnessResponseListener;
import cy.agorise.graphenej.models.ApiCall;
import cy.agorise.graphenej.models.WitnessResponse;
/**
* Class that implements get_key_references request handler.
*
* Retrieve the keys that refer to the address/list of addresses.
*
* The request returns all accounts that refer to the key or account id in their owner or active authorities.
*
* @see <a href="https://goo.gl/np8CYF">get_key_references API doc</a>
*/
public class GetKeyReferences extends BaseGrapheneHandler {
private List<Address> addresses;
private boolean mOneTime;
/**
* Constructor
*
* @param address address to be query
* @param oneTime boolean value indicating if WebSocket must be closed (true) or not (false)
* after the response
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the success/failure
* of the transaction broadcast operation.
*/
public GetKeyReferences(Address address, boolean oneTime, WitnessResponseListener listener){
super(listener);
addresses = new ArrayList<>();
addresses.add(address);
this.mOneTime = oneTime;
}
/**
*
* @param addresses list of addresses to be query
* @param oneTime boolean value indicating if WebSocket must be closed (true) or not (false)
* after the response
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the success/failure
* of the transaction broadcast operation.
*/
public GetKeyReferences(List<Address> addresses, boolean oneTime, WitnessResponseListener listener) {
super(listener);
this.addresses = addresses;
this.mListener = listener;
this.mOneTime = oneTime;
}
/**
* Using this constructor the websocket connection closes after the response.
*
* @param address
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the success/failure
* of the transaction broadcast operation.
*/
public GetKeyReferences(Address address, WitnessResponseListener listener){
this(address, true, listener);
}
/**
* Using this constructor the websocket connection closes after the response.
*
* @param addresses
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the success/failure
* of the transaction broadcast operation.
*/
public GetKeyReferences(List<Address> addresses, WitnessResponseListener listener) {
this(addresses, true, listener);
}
@Override
public void onConnected(WebSocket websocket, Map<String, List<String>> headers) throws Exception {
ArrayList<Serializable> inner = new ArrayList();
for(Address addr : addresses){
inner.add(addr.toString());
}
ArrayList<Serializable> params = new ArrayList<>();
params.add(inner);
ApiCall getAccountByAddress = new ApiCall(0, RPC.CALL_GET_KEY_REFERENCES, params, RPC.VERSION, 1);
websocket.sendText(getAccountByAddress.toJsonString());
}
@Override
public void onTextFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
System.out.println("<<< "+frame.getPayloadText());
String response = frame.getPayloadText();
GsonBuilder builder = new GsonBuilder();
Type GetAccountByAddressResponse = new TypeToken<WitnessResponse<List<List<UserAccount>>>>(){}.getType();
builder.registerTypeAdapter(UserAccount.class, new UserAccount.UserAccountSimpleDeserializer());
WitnessResponse<List<List<UserAccount>>> witnessResponse = builder.create().fromJson(response, GetAccountByAddressResponse);
if (witnessResponse.error != null) {
this.mListener.onError(witnessResponse.error);
} else {
this.mListener.onSuccess(witnessResponse);
}
if(mOneTime){
websocket.disconnect();
}
}
@Override
public void onFrameSent(WebSocket websocket, WebSocketFrame frame) throws Exception {
if(frame.isTextFrame())
System.out.println(">>> "+frame.getPayloadText());
}
}

View file

@ -1,137 +0,0 @@
package cy.agorise.graphenej.api;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.neovisionaries.ws.client.WebSocket;
import com.neovisionaries.ws.client.WebSocketException;
import com.neovisionaries.ws.client.WebSocketFrame;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import cy.agorise.graphenej.AssetAmount;
import cy.agorise.graphenej.LimitOrder;
import cy.agorise.graphenej.RPC;
import cy.agorise.graphenej.UserAccount;
import cy.agorise.graphenej.interfaces.WitnessResponseListener;
import cy.agorise.graphenej.models.ApiCall;
import cy.agorise.graphenej.models.BaseResponse;
import cy.agorise.graphenej.models.WitnessResponse;
/**
* Class that implements get_limit_orders request handler.
*
* Get limit orders in a given market.
*
* The request returns the limit orders, ordered from least price to greatest
*
* @see <a href="https://goo.gl/5sRTRq">get_limit_orders API doc</a>
*
*/
public class GetLimitOrders extends BaseGrapheneHandler {
private String a;
private String b;
private int limit;
private WitnessResponseListener mListener;
private boolean mOneTime;
/**
* Default Constructor
*
* @param a id of asset being sold
* @param b id of asset being purchased
* @param limit maximum number of orders to retrieve
* @param oneTime boolean value indicating if WebSocket must be closed (true) or not
* (false) after the response
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public GetLimitOrders(String a, String b, int limit, boolean oneTime, WitnessResponseListener listener) {
super(listener);
this.a = a;
this.b = b;
this.limit = limit;
this.mOneTime = oneTime;
this.mListener = listener;
}
/**
* Using this constructor the WebSocket connection closes after the response.
*
* @param a id of asset being sold
* @param b id of asset being purchased
* @param limit maximum number of orders to retrieve
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public GetLimitOrders(String a, String b, int limit, WitnessResponseListener listener) {
this(a, b, limit, true, listener);
}
@Override
public void onConnected(WebSocket websocket, Map<String, List<String>> headers) throws Exception {
ArrayList<Serializable> accountParams = new ArrayList<>();
accountParams.add(this.a);
accountParams.add(this.b);
accountParams.add(this.limit);
ApiCall getAccountByName = new ApiCall(0, RPC.CALL_GET_LIMIT_ORDERS, accountParams, RPC.VERSION, 1);
websocket.sendText(getAccountByName.toJsonString());
}
@Override
public void onTextFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
if(frame.isTextFrame())
System.out.println("<<< "+frame.getPayloadText());
try {
String response = frame.getPayloadText();
GsonBuilder builder = new GsonBuilder();
Type GetLimitOrdersResponse = new TypeToken<WitnessResponse<List<LimitOrder>>>() {}.getType();
builder.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer());
builder.registerTypeAdapter(UserAccount.class, new UserAccount.UserAccountSimpleDeserializer());
builder.registerTypeAdapter(LimitOrder.class, new LimitOrder.LimitOrderDeserializer());
WitnessResponse<List<LimitOrder>> witnessResponse = builder.create().fromJson(response, GetLimitOrdersResponse);
if (witnessResponse.error != null) {
this.mListener.onError(witnessResponse.error);
} else {
this.mListener.onSuccess(witnessResponse);
}
} catch (Exception e) {
e.printStackTrace();
}
if(mOneTime){
websocket.disconnect();
}
}
@Override
public void onFrameSent(WebSocket websocket, WebSocketFrame frame) throws Exception {
if(frame.isTextFrame()){
System.out.println(">>> "+frame.getPayloadText());
}
}
@Override
public void onError(WebSocket websocket, WebSocketException cause) throws Exception {
mListener.onError(new BaseResponse.Error(cause.getMessage()));
if(mOneTime){
websocket.disconnect();
}
}
@Override
public void handleCallbackError(WebSocket websocket, Throwable cause) throws Exception {
mListener.onError(new BaseResponse.Error(cause.getMessage()));
if(mOneTime){
websocket.disconnect();
}
}
}

View file

@ -1,243 +0,0 @@
package cy.agorise.graphenej.api;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.neovisionaries.ws.client.WebSocket;
import com.neovisionaries.ws.client.WebSocketFrame;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import cy.agorise.graphenej.Asset;
import cy.agorise.graphenej.RPC;
import cy.agorise.graphenej.interfaces.WitnessResponseListener;
import cy.agorise.graphenej.models.ApiCall;
import cy.agorise.graphenej.models.BaseResponse;
import cy.agorise.graphenej.models.BucketObject;
import cy.agorise.graphenej.models.WitnessResponse;
/**
* Class that implements get_market_history request handler.
*
* Get mar
*
* @see <a href="https://goo.gl/hfVFBW">get_market_history API doc</a>
*
*/
public class GetMarketHistory extends BaseGrapheneHandler {
// Sequence of message ids
private final static int LOGIN_ID = 1;
private final static int GET_HISTORY_ID = 2;
private final static int GET_HISTORY_DATA = 3;
// API call parameters
private Asset base;
private Asset quote;
private long bucket;
private Date start;
private Date end;
private WitnessResponseListener mListener;
private WebSocket mWebsocket;
private int currentId = 1;
private int apiId = -1;
private int counter = 0;
private boolean mOneTime;
/**
* Default Constructor
*
* @param base asset which history is desired
* @param quote asset which the base price asset will be compared to
* @param bucket the time interval (in seconds) for each point should be (analog to
* candles on a candle stick graph).
* Note: The bucket value is discrete and node dependent. The default value
* is 3600s. To get the available buckets of a node use
* get_all_asset_holders API call.
* @param start datetime of of the most recent operation to retrieve (Note: The name is
* counter intuitive, but it follow the original API parameter name)
* @param end datetime of the the earliest operation to retrieve
* @param oneTime boolean value indicating if WebSocket must be closed (true) or not
* (false) after the response
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public GetMarketHistory(Asset base, Asset quote, long bucket, Date start, Date end, boolean oneTime, WitnessResponseListener listener){
super(listener);
this.base = base;
this.quote = quote;
this.bucket = bucket;
this.start = start;
this.end = end;
this.mOneTime = oneTime;
this.mListener = listener;
}
/**
* Using this constructor the WebSocket connection closes after the response.
*
* @param base asset which history is desired
* @param quote asset which the base price asset will be compared to
* @param bucket the time interval (in seconds) for each point should be (analog to
* candles on a candle stick graph).
* Note: The bucket value is discrete and node dependent. The default value
* is 3600s. To get the available buckets of a node use
* get_all_asset_holders API call.
* @param start datetime of of the most recent operation to retrieve (Note: The name is
* counter intuitive, but it follow the original API parameter name)
* @param end datetime of the the earliest operation to retrieve
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public GetMarketHistory(Asset base, Asset quote, long bucket, Date start, Date end, WitnessResponseListener listener){
this(base, quote, bucket, start, end, true, listener);
}
public Asset getBase() {
return base;
}
public void setBase(Asset base) {
this.base = base;
}
public Asset getQuote() {
return quote;
}
public void setQuote(Asset quote) {
this.quote = quote;
}
public long getBucket() {
return bucket;
}
public void setBucket(long bucket) {
this.bucket = bucket;
}
public Date getStart() {
return start;
}
public void setStart(Date start) {
this.start = start;
}
public Date getEnd() {
return end;
}
public void setEnd(Date end) {
this.end = end;
}
public int getCount(){
return this.counter;
}
public void disconnect(){
if(mWebsocket != null && mWebsocket.isOpen()){
mWebsocket.disconnect();
}
}
/**
* Retries the 'get_market_history' API call.
* Hopefully with different 'start' and 'stop' parameters.
*/
public void retry(){
sendHistoricalMarketDataRequest();
}
@Override
public void onConnected(WebSocket websocket, Map<String, List<String>> headers) throws Exception {
mWebsocket = websocket;
ArrayList<Serializable> loginParams = new ArrayList<>();
loginParams.add(null);
loginParams.add(null);
ApiCall loginCall = new ApiCall(1, RPC.CALL_LOGIN, loginParams, RPC.VERSION, currentId);
websocket.sendText(loginCall.toJsonString());
}
@Override
public void onTextFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
String response = frame.getPayloadText();
System.out.println("<<< "+response);
Gson gson = new Gson();
BaseResponse baseResponse = gson.fromJson(response, BaseResponse.class);
if(baseResponse.error != null){
mListener.onError(baseResponse.error);
if(mOneTime){
websocket.disconnect();
}
}else{
currentId++;
ArrayList<Serializable> emptyParams = new ArrayList<>();
if(baseResponse.id == LOGIN_ID){
ApiCall getRelativeAccountHistoryId = new ApiCall(1, RPC.CALL_HISTORY, emptyParams, RPC.VERSION, currentId);
websocket.sendText(getRelativeAccountHistoryId.toJsonString());
} else if(baseResponse.id == GET_HISTORY_ID){
Type ApiIdResponse = new TypeToken<WitnessResponse<Integer>>() {}.getType();
WitnessResponse<Integer> witnessResponse = gson.fromJson(response, ApiIdResponse);
apiId = witnessResponse.result.intValue();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
ArrayList<Serializable> params = new ArrayList<>();
params.add(this.base.getObjectId());
params.add(this.quote.getObjectId());
params.add(this.bucket);
params.add(dateFormat.format(this.start));
params.add(dateFormat.format(this.end));
ApiCall getRelativeAccountHistoryCall = new ApiCall(apiId, RPC.CALL_GET_MARKET_HISTORY, params, RPC.VERSION, currentId);
websocket.sendText(getRelativeAccountHistoryCall.toJsonString());
}else if(baseResponse.id >= GET_HISTORY_DATA){
GsonBuilder builder = new GsonBuilder();
Type MarketHistoryResponse = new TypeToken<WitnessResponse<List<BucketObject>>>(){}.getType();
builder.registerTypeAdapter(BucketObject.class, new BucketObject.BucketDeserializer());
WitnessResponse<List<BucketObject>> marketHistoryResponse = builder.create().fromJson(response, MarketHistoryResponse);
mListener.onSuccess(marketHistoryResponse);
}
}
}
/**
* Actually sends the 'get_market_history' API call request. This method might be called multiple
* times during the life-cycle of this instance because we might not have gotten anything
* in the first requested interval.
*/
private void sendHistoricalMarketDataRequest(){
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
ArrayList<Serializable> params = new ArrayList<>();
params.add(this.base.getObjectId());
params.add(this.quote.getObjectId());
params.add(this.bucket);
params.add(dateFormat.format(this.start));
params.add(dateFormat.format(this.end));
ApiCall getRelativeAccountHistoryCall = new ApiCall(apiId, RPC.CALL_GET_MARKET_HISTORY, params, RPC.VERSION, currentId);
mWebsocket.sendText(getRelativeAccountHistoryCall.toJsonString());
counter++;
}
@Override
public void onFrameSent(WebSocket websocket, WebSocketFrame frame) throws Exception {
if(frame.isTextFrame())
System.out.println(">>> "+frame.getPayloadText());
}
}

View file

@ -1,139 +0,0 @@
package cy.agorise.graphenej.api;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
import com.neovisionaries.ws.client.WebSocket;
import com.neovisionaries.ws.client.WebSocketFrame;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import cy.agorise.graphenej.AccountOptions;
import cy.agorise.graphenej.Asset;
import cy.agorise.graphenej.AssetAmount;
import cy.agorise.graphenej.Authority;
import cy.agorise.graphenej.GrapheneObject;
import cy.agorise.graphenej.RPC;
import cy.agorise.graphenej.UserAccount;
import cy.agorise.graphenej.interfaces.WitnessResponseListener;
import cy.agorise.graphenej.models.ApiCall;
import cy.agorise.graphenej.models.BitAssetData;
import cy.agorise.graphenej.models.WitnessResponse;
/**
*
* Class that implements get_objects request handler.
*
* Get the objects corresponding to the provided IDs.
*
* The response returns a list of objects retrieved, in the order they are mentioned in ids
*
* @see <a href="https://goo.gl/isRfeg">get_objects API doc</a>
*
*/
public class GetObjects extends BaseGrapheneHandler {
private List<String> ids;
private boolean mOneTime;
/**
* Default Constructor
*
* @param ids list of IDs of the objects to retrieve
* @param oneTime boolean value indicating if WebSocket must be closed (true) or not
* (false) after the response
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public GetObjects(List<String> ids, boolean oneTime, WitnessResponseListener listener){
super(listener);
this.ids = ids;
this.mOneTime = oneTime;
}
/**
* Using this constructor the WebSocket connection closes after the response.
*
* @param ids list of IDs of the objects to retrieve
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public GetObjects(List<String> ids, WitnessResponseListener listener){
this(ids, true, listener);
}
@Override
public void onConnected(WebSocket websocket, Map<String, List<String>> headers) throws Exception {
ArrayList<Serializable> params = new ArrayList<>();
ArrayList<Serializable> subParams = new ArrayList<>();
for(String id : this.ids){
subParams.add(id);
}
params.add(subParams);
ApiCall apiCall = new ApiCall(0, RPC.GET_OBJECTS, params, RPC.VERSION, 0);
websocket.sendText(apiCall.toJsonString());
}
@Override
public void onTextFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
if(frame.isTextFrame()){
System.out.println("<< "+frame.getPayloadText());
}
String response = frame.getPayloadText();
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer());
gsonBuilder.registerTypeAdapter(Asset.class, new Asset.AssetDeserializer());
gsonBuilder.registerTypeAdapter(UserAccount.class, new UserAccount.UserAccountFullDeserializer());
gsonBuilder.registerTypeAdapter(Authority.class, new Authority.AuthorityDeserializer());
gsonBuilder.registerTypeAdapter(AccountOptions.class, new AccountOptions.AccountOptionsDeserializer());
Gson gson = gsonBuilder.create();
List<GrapheneObject> parsedResult = new ArrayList<>();
JsonParser parser = new JsonParser();
JsonArray resultArray = parser.parse(response).getAsJsonObject().get(WitnessResponse.KEY_RESULT).getAsJsonArray();
for(JsonElement element : resultArray){
String id = element.getAsJsonObject().get(GrapheneObject.KEY_ID).getAsString();
GrapheneObject grapheneObject = new GrapheneObject(id);
switch (grapheneObject.getObjectType()){
case ASSET_OBJECT:
Asset asset = gson.fromJson(element, Asset.class);
parsedResult.add(asset);
break;
case ACCOUNT_OBJECT:
UserAccount account = gson.fromJson(element, UserAccount.class);
parsedResult.add(account);
break;
case ASSET_BITASSET_DATA:
Type BitAssetDataType = new TypeToken<WitnessResponse<List<BitAssetData>>>(){}.getType();
WitnessResponse<List<BitAssetData>> witnessResponse = gsonBuilder.create().fromJson(response, BitAssetDataType);
BitAssetData bitAssetData = witnessResponse.result.get(0);
parsedResult.add(bitAssetData);
}
}
WitnessResponse<List<GrapheneObject>> output = new WitnessResponse<>();
output.result = parsedResult;
mListener.onSuccess(output);
if(mOneTime){
websocket.disconnect();
}
}
@Override
public void onFrameSent(WebSocket websocket, WebSocketFrame frame) throws Exception {
if(frame.isTextFrame()){
System.out.println(">> "+frame.getPayloadText());
}
}
}

View file

@ -1,214 +0,0 @@
package cy.agorise.graphenej.api;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.neovisionaries.ws.client.WebSocket;
import com.neovisionaries.ws.client.WebSocketFrame;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import cy.agorise.graphenej.AssetAmount;
import cy.agorise.graphenej.RPC;
import cy.agorise.graphenej.UserAccount;
import cy.agorise.graphenej.interfaces.WitnessResponseListener;
import cy.agorise.graphenej.models.ApiCall;
import cy.agorise.graphenej.models.BaseResponse;
import cy.agorise.graphenej.models.HistoricalTransfer;
import cy.agorise.graphenej.models.WitnessResponse;
import cy.agorise.graphenej.objects.Memo;
import cy.agorise.graphenej.operations.TransferOperation;
/**
* Class used to encapsulate the communication sequence used to retrieve the transaction history of
* a given user.
*/
public class GetRelativeAccountHistory extends BaseGrapheneHandler {
// Sequence of message ids
private final static int LOGIN_ID = 1;
private final static int GET_HISTORY_ID = 2;
private final static int GET_HISTORY_DATA = 3;
// Default value constants
public static final int DEFAULT_STOP = 0;
public static final int DEFAULT_START = 0;
public static final int MAX_LIMIT = 100;
// API call parameters
private UserAccount mUserAccount;
private int stop;
private int limit;
private int start;
private WitnessResponseListener mListener;
private WebSocket mWebsocket;
private int currentId = 1;
private int apiId = -1;
private boolean mOneTime;
/**
* Constructor that takes all possible parameters.
*
* @param userAccount The user account to be queried
* @param stop Sequence number of earliest operation
* @param limit Maximum number of operations to retrieve (must not exceed 100)
* @param start Sequence number of the most recent operation to retrieve
* @param oneTime boolean value indicating if WebSocket must be closed (true) or not
* (false) after the response
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public GetRelativeAccountHistory(UserAccount userAccount, int stop, int limit, int start, boolean oneTime, WitnessResponseListener listener){
super(listener);
if(limit > MAX_LIMIT) limit = MAX_LIMIT;
this.mUserAccount = userAccount;
this.stop = stop;
this.limit = limit;
this.start = start;
this.mOneTime = oneTime;
this.mListener = listener;
}
/**
* Constructor that uses the default values, and sets the limit to its maximum possible value.
*
* @param userAccount The user account to be queried
* @param oneTime boolean value indicating if WebSocket must be closed (true) or not
* (false) after the response
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public GetRelativeAccountHistory(UserAccount userAccount, boolean oneTime, WitnessResponseListener listener){
super(listener);
this.mUserAccount = userAccount;
this.stop = DEFAULT_STOP;
this.limit = MAX_LIMIT;
this.start = DEFAULT_START;
this.mOneTime = oneTime;
this.mListener = listener;
}
/**
* Constructor that takes all possible parameters for the query.
* Using this constructor the WebSocket connection closes after the response.
*
* @param userAccount The user account to be queried
* @param stop Sequence number of earliest operation
* @param limit Maximum number of operations to retrieve (must not exceed 100)
* @param start Sequence number of the most recent operation to retrieve
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public GetRelativeAccountHistory(UserAccount userAccount, int stop, int limit, int start, WitnessResponseListener listener){
this(userAccount, stop, limit, start, true, listener);
}
/**
* Constructor that uses the default values, and sets the limit to its maximum possible value.
* Using this constructor the WebSocket connection closes after the response.
*
* @param userAccount The user account to be queried
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public GetRelativeAccountHistory(UserAccount userAccount, WitnessResponseListener listener){
this(userAccount, true, listener);
}
@Override
public void onConnected(WebSocket websocket, Map<String, List<String>> headers) throws Exception {
mWebsocket = websocket;
ArrayList<Serializable> loginParams = new ArrayList<>();
loginParams.add(null);
loginParams.add(null);
ApiCall loginCall = new ApiCall(1, RPC.CALL_LOGIN, loginParams, RPC.VERSION, currentId);
websocket.sendText(loginCall.toJsonString());
}
@Override
public void onTextFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
String response = frame.getPayloadText();
System.out.println("<<< "+response);
Gson gson = new Gson();
BaseResponse baseResponse = gson.fromJson(response, BaseResponse.class);
if(baseResponse.error != null){
mListener.onError(baseResponse.error);
if(mOneTime){
websocket.disconnect();
}
}else{
currentId++;
ArrayList<Serializable> emptyParams = new ArrayList<>();
if(baseResponse.id == LOGIN_ID){
ApiCall getRelativeAccountHistoryId = new ApiCall(1, RPC.CALL_HISTORY, emptyParams, RPC.VERSION, currentId);
websocket.sendText(getRelativeAccountHistoryId.toJsonString());
}else if(baseResponse.id == GET_HISTORY_ID){
Type ApiIdResponse = new TypeToken<WitnessResponse<Integer>>() {}.getType();
WitnessResponse<Integer> witnessResponse = gson.fromJson(response, ApiIdResponse);
apiId = witnessResponse.result.intValue();
sendRelativeAccountHistoryRequest();
}else if(baseResponse.id >= GET_HISTORY_DATA){
Type RelativeAccountHistoryResponse = new TypeToken<WitnessResponse<List<HistoricalTransfer>>>(){}.getType();
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(TransferOperation.class, new TransferOperation.TransferDeserializer());
gsonBuilder.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer());
gsonBuilder.registerTypeAdapter(Memo.class, new Memo.MemoDeserializer());
WitnessResponse<List<HistoricalTransfer>> transfersResponse = gsonBuilder.create().fromJson(response, RelativeAccountHistoryResponse);
mListener.onSuccess(transfersResponse);
}
}
}
/**
* Sends the actual get_relative_account_history request.
*/
private void sendRelativeAccountHistoryRequest(){
ArrayList<Serializable> params = new ArrayList<>();
params.add(mUserAccount.getObjectId());
params.add(this.stop);
params.add(this.limit);
params.add(this.start);
ApiCall getRelativeAccountHistoryCall = new ApiCall(apiId, RPC.CALL_GET_RELATIVE_ACCOUNT_HISTORY, params, RPC.VERSION, currentId);
mWebsocket.sendText(getRelativeAccountHistoryCall.toJsonString());
}
/**
* Updates the arguments and makes a new call to the get_relative_account_history API.
*
* @param stop Sequence number of earliest operation
* @param limit Maximum number of operations to retrieve (must not exceed 100)
* @param start Sequence number of the most recent operation to retrieve
*/
public void retry(int stop, int limit, int start){
this.stop = stop;
this.limit = limit;
this.start = start;
sendRelativeAccountHistoryRequest();
}
/**
* Disconnects the WebSocket.
*/
public void disconnect(){
if(mWebsocket != null && mWebsocket.isOpen() && mOneTime){
mWebsocket.disconnect();
}
}
@Override
public void onFrameSent(WebSocket websocket, WebSocketFrame frame) throws Exception {
if(frame.isTextFrame())
System.out.println(">>> "+frame.getPayloadText());
}
}

View file

@ -1,113 +0,0 @@
package cy.agorise.graphenej.api;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import cy.agorise.graphenej.AssetAmount;
import cy.agorise.graphenej.RPC;
import cy.agorise.graphenej.interfaces.WitnessResponseListener;
import cy.agorise.graphenej.models.ApiCall;
import cy.agorise.graphenej.models.BaseResponse;
import cy.agorise.graphenej.models.WitnessResponse;
import com.neovisionaries.ws.client.WebSocket;
import com.neovisionaries.ws.client.WebSocketAdapter;
import com.neovisionaries.ws.client.WebSocketException;
import com.neovisionaries.ws.client.WebSocketFrame;
import cy.agorise.graphenej.Asset;
import cy.agorise.graphenej.BaseOperation;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Class that implements get_required_fees request handler.
*
* For each operation calculate the required fee in the specified asset type.
*
* @see <a href="https://goo.gl/MB4TXq">get_required_fees API doc</a>
*/
public class GetRequiredFees extends BaseGrapheneHandler {
private WitnessResponseListener mListener;
private List<BaseOperation> operations;
private Asset asset;
private boolean mOneTime;
/**
* Default Constructor
*
* @param operations list of operations that fee should be calculated
* @param asset specify the asset of the operations
* @param oneTime boolean value indicating if WebSocket must be closed (true) or not
* (false) after the response
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public GetRequiredFees(List<BaseOperation> operations, Asset asset, boolean oneTime, WitnessResponseListener listener){
super(listener);
this.operations = operations;
this.asset = asset;
this.mOneTime = oneTime;
this.mListener = listener;
}
/**
* Using this constructor the WebSocket connection closes after the response.
*
* @param operations list of operations that fee should be calculated
* @param asset specify the asset of the operations
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public GetRequiredFees(List<BaseOperation> operations, Asset asset, WitnessResponseListener listener){
this(operations, asset, true, listener);
}
@Override
public void onConnected(WebSocket websocket, Map<String, List<String>> headers) throws Exception {
ArrayList<Serializable> accountParams = new ArrayList<>();
accountParams.add((Serializable) this.operations);
accountParams.add(this.asset.getObjectId());
ApiCall getRequiredFees = new ApiCall(0, RPC.CALL_GET_REQUIRED_FEES, accountParams, RPC.VERSION, 1);
websocket.sendText(getRequiredFees.toJsonString());
}
@Override
public void onTextFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
String response = frame.getPayloadText();
Gson gson = new Gson();
Type GetRequiredFeesResponse = new TypeToken<WitnessResponse<List<AssetAmount>>>(){}.getType();
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer());
WitnessResponse<List<AssetAmount>> witnessResponse = gsonBuilder.create().fromJson(response, GetRequiredFeesResponse);
if(witnessResponse.error != null){
mListener.onError(witnessResponse.error);
}else{
mListener.onSuccess(witnessResponse);
}
}
@Override
public void onError(WebSocket websocket, WebSocketException cause) throws Exception {
mListener.onError(new BaseResponse.Error(cause.getMessage()));
if(mOneTime){
websocket.disconnect();
}
}
@Override
public void handleCallbackError(WebSocket websocket, Throwable cause) throws Exception {
mListener.onError(new BaseResponse.Error(cause.getMessage()));
if(mOneTime){
websocket.disconnect();
}
}
}

View file

@ -1,127 +0,0 @@
package cy.agorise.graphenej.api;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.neovisionaries.ws.client.WebSocket;
import com.neovisionaries.ws.client.WebSocketFrame;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import cy.agorise.graphenej.MarketTrade;
import cy.agorise.graphenej.RPC;
import cy.agorise.graphenej.interfaces.WitnessResponseListener;
import cy.agorise.graphenej.models.ApiCall;
import cy.agorise.graphenej.models.WitnessResponse;
/**
* Class that implements get_trade_history request handler.
*
* Get recent trades for the market assetA:assetB for a time interval
* Note: Currently, timezone offsets are not supported. The time must be UTC.
*
* The request returns the all trades of the passed pair of asset at a specific time interval.
*
* @see <a href="https://goo.gl/Y1x3bE">get_trade_history API doc</a>
*
*/
public class GetTradeHistory extends BaseGrapheneHandler {
private String a;
private String b;
private String toTime;
private String fromTime;
private int limit;
private WitnessResponseListener mListener;
private boolean mOneTime;
/**
* Constructor
*
* @param a name of the first asset
* @param b name of the second asset
* @param toTime stop time as a UNIX timestamp
* @param fromTime start time as a UNIX timestamp
* @param limit number of transactions to retrieve, capped at 100
* @param oneTime boolean value indicating if WebSocket must be closed (true) or not
* (false) after the response
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public GetTradeHistory(String a, String b, String toTime, String fromTime,int limit, boolean oneTime, WitnessResponseListener listener) {
super(listener);
this.a = a;
this.b = b;
this.toTime = toTime;
this.fromTime = fromTime;
this.limit = limit;
this.mOneTime = oneTime;
this.mListener = listener;
}
/**
* Using this constructor the WebSocket connection closes after the response.
*
* @param a name of the first asset
* @param b name of the second asset
* @param toTime stop time as a UNIX timestamp
* @param fromTime start time as a UNIX timestamp
* @param limit number of transactions to retrieve, capped at 100
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public GetTradeHistory(String a, String b, String toTime, String fromTime,int limit, WitnessResponseListener listener) {
this(a, b, toTime, fromTime, limit, true, listener);
}
@Override
public void onConnected(WebSocket websocket, Map<String, List<String>> headers) throws Exception {
ArrayList<Serializable> accountParams = new ArrayList<>();
accountParams.add(this.a);
accountParams.add(this.b);
accountParams.add(this.toTime);
accountParams.add(this.fromTime);
accountParams.add(this.limit);
ApiCall getAccountByName = new ApiCall(0, RPC.CALL_GET_TRADE_HISTORY, accountParams, RPC.VERSION, 1);
websocket.sendText(getAccountByName.toJsonString());
}
@Override
public void onTextFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
if (frame.isTextFrame()) {
System.out.println("<<< " + frame.getPayloadText());
}
try {
String response = frame.getPayloadText();
GsonBuilder builder = new GsonBuilder();
Type GetTradeHistoryResponse = new TypeToken<WitnessResponse<List<MarketTrade>>>() {
}.getType();
WitnessResponse<List<MarketTrade>> witnessResponse = builder.create().fromJson(response, GetTradeHistoryResponse);
if (witnessResponse.error != null) {
this.mListener.onError(witnessResponse.error);
} else {
this.mListener.onSuccess(witnessResponse);
}
} catch (Exception e) {
e.printStackTrace();
}
if(mOneTime){
websocket.disconnect();
}
}
@Override
public void onFrameSent(WebSocket websocket, WebSocketFrame frame) throws Exception {
if (frame.isTextFrame()) {
System.out.println(">>> " + frame.getPayloadText());
}
}
}

View file

@ -1,146 +0,0 @@
package cy.agorise.graphenej.api;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.neovisionaries.ws.client.WebSocket;
import com.neovisionaries.ws.client.WebSocketFrame;
import cy.agorise.graphenej.Asset;
import cy.agorise.graphenej.RPC;
import cy.agorise.graphenej.interfaces.WitnessResponseListener;
import cy.agorise.graphenej.models.ApiCall;
import cy.agorise.graphenej.models.WitnessResponse;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* WebSocketAdapter class used to send a request a 'list_assets' API call to the witness node.
*
* The API imposes a limit of of 100 assets per request, but if the user of this class wants
* to get a list of all assets, the LIST_ALL constant must be used as second argument in the
* constructor. Internally we are going to perform multiple calls in order to satisfy the user's
* request.
*
* @see: <a href="http://docs.bitshares.org/development/namespaces/app.html"></a>
*/
public class ListAssets extends BaseGrapheneHandler {
/**
* Constant that must be used as argument to the constructor of this class to indicate
* that the user wants to get all existing assets.
*/
public static final int LIST_ALL = -1;
/**
* Internal constant used to represent the maximum limit of assets retrieved in one call.
*/
private final int MAX_BATCH_SIZE = 100;
private List<Asset> assets;
private String lowerBound;
private int limit;
private int requestCounter = 0;
private boolean mOneTime;
/**
* Constructor
*
* @param lowerBoundSymbol Lower bound of symbol names to retrieve
* @param limit Maximum number of assets to fetch, if the constant LIST_ALL
* is passed, all existing assets will be retrieved.
* @param oneTime boolean value indicating if WebSocket must be closed (true) or not
* (false) after the response
* @param listener A class implementing the WitnessResponseListener interface. This
* should be implemented by the party interested in being notified
* about the success/failure of the operation.
*/
public ListAssets(String lowerBoundSymbol, int limit, boolean oneTime, WitnessResponseListener listener){
super(listener);
this.lowerBound = lowerBoundSymbol;
this.limit = limit;
this.mOneTime = oneTime;
}
/**
* Using this constructor the WebSocket connection closes after the response.
*
* @param lowerBoundSymbol Lower bound of symbol names to retrieve
* @param limit Maximum number of assets to fetch, if the constant LIST_ALL
* is passed, all existing assets will be retrieved.
* @param oneTime boolean value indicating if WebSocket must be closed (true) or not
* (false) after the response
* @param listener A class implementing the WitnessResponseListener interface. This
* should be implemented by the party interested in being notified
* about the success/failure of the operation.
*/
public ListAssets(String lowerBoundSymbol, int limit, WitnessResponseListener listener){
this(lowerBoundSymbol, limit, true, listener);
}
@Override
public void onConnected(WebSocket websocket, Map<String, List<String>> headers) throws Exception {
ArrayList<Serializable> params = new ArrayList<>();
params.add(this.lowerBound);
if(limit > MAX_BATCH_SIZE || limit == LIST_ALL){
params.add(MAX_BATCH_SIZE);
}else{
params.add(this.limit);
}
ApiCall apiCall = new ApiCall(0, RPC.CALL_LIST_ASSETS, params, RPC.VERSION, 0);
websocket.sendText(apiCall.toJsonString());
}
@Override
public void onTextFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
String response = frame.getPayloadText();
GsonBuilder gsonBuilder = new GsonBuilder();
Type LookupAssetSymbolsResponse = new TypeToken<WitnessResponse<List<Asset>>>(){}.getType();
gsonBuilder.registerTypeAdapter(Asset.class, new Asset.AssetDeserializer());
WitnessResponse<List<Asset>> witnessResponse = gsonBuilder.create().fromJson(response, LookupAssetSymbolsResponse);
if(this.limit != LIST_ALL && this.limit < MAX_BATCH_SIZE){
// If the requested number of assets was below
// the limit, we just call the listener.
mListener.onSuccess(witnessResponse);
if(mOneTime){
websocket.disconnect();
}
}else{
// Updating counter to keep track of how many batches we already retrieved.
requestCounter++;
if(this.assets == null){
this.assets = new ArrayList<>();
}
this.assets.addAll(witnessResponse.result);
// Checking to see if we're done
if(this.limit == LIST_ALL && witnessResponse.result.size() < MAX_BATCH_SIZE){
// In case we requested all assets, we might be in the last round whenever
// we got less than the requested amount.
witnessResponse.result = this.assets;
mListener.onSuccess(witnessResponse);
if(mOneTime){
websocket.disconnect();
}
}else if(this.assets.size() == this.limit){
// We already have the required amount of assets
witnessResponse.result = this.assets;
mListener.onSuccess(witnessResponse);
if(mOneTime){
websocket.disconnect();
}
}else{
// We still need to fetch some more assets
this.lowerBound = this.assets.get(this.assets.size() - 1).getSymbol();
int nextBatch = this.limit == LIST_ALL ? MAX_BATCH_SIZE : this.limit - (MAX_BATCH_SIZE * requestCounter);
ArrayList<Serializable> params = new ArrayList<>();
params.add(this.lowerBound);
params.add(nextBatch);
ApiCall apiCall = new ApiCall(0, RPC.CALL_LIST_ASSETS, params, RPC.VERSION, 0);
websocket.sendText(apiCall.toJsonString());
}
}
}
}

View file

@ -1,134 +0,0 @@
package cy.agorise.graphenej.api;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.neovisionaries.ws.client.WebSocket;
import com.neovisionaries.ws.client.WebSocketFrame;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import cy.agorise.graphenej.RPC;
import cy.agorise.graphenej.UserAccount;
import cy.agorise.graphenej.interfaces.WitnessResponseListener;
import cy.agorise.graphenej.models.ApiCall;
import cy.agorise.graphenej.models.WitnessResponse;
/**
* Class that implements lookup_accounts request handler.
*
* Get names and IDs for registered accounts.
*
* The request returns a map of account names to corresponding IDs.
*
* @see <a href="https://goo.gl/zhPjuW">lookup_accounts API doc</a>
*/
public class LookupAccounts extends BaseGrapheneHandler {
public static final int DEFAULT_MAX = 1000;
private final String accountName;
private int maxAccounts = DEFAULT_MAX;
private final WitnessResponseListener mListener;
private boolean mOneTime;
/**
* Constructor
*
* @param accountName account name used at the query
* @param oneTime boolean value indicating if WebSocket must be closed (true) or not
* (false) after the response
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public LookupAccounts(String accountName, boolean oneTime, WitnessResponseListener listener){
super(listener);
this.accountName = accountName;
this.maxAccounts = DEFAULT_MAX;
this.mOneTime = oneTime;
this.mListener = listener;
}
/**
* Constructor with maxAccounts
*
* @param accountName account name used at the query
* @param maxAccounts maximum number of results to return (must not exceed 1000)
* @param oneTime boolean value indicating if WebSocket must be closed (true) or not
* (false) after the response
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public LookupAccounts(String accountName, int maxAccounts, boolean oneTime, WitnessResponseListener listener){
super(listener);
this.accountName = accountName;
this.maxAccounts = maxAccounts;
this.mOneTime = oneTime;
this.mListener = listener;
}
/**
* Using this constructor the WebSocket connection closes after the response.
*
* @param accountName account name used at the query
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public LookupAccounts(String accountName, WitnessResponseListener listener){
this(accountName, true, listener);
}
/**
* Using this constructor the WebSocket connection closes after the response.
*
* @param accountName account name used at the query
* @param maxAccounts maximum number of results to return (must not exceed 1000)
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public LookupAccounts(String accountName, int maxAccounts, WitnessResponseListener listener){
this(accountName, maxAccounts, true, listener);
}
@Override
public void onConnected(WebSocket websocket, Map<String, List<String>> headers) throws Exception {
ArrayList<Serializable> accountParams = new ArrayList<>();
accountParams.add(this.accountName);
accountParams.add(this.maxAccounts);
ApiCall getAccountByName = new ApiCall(0, RPC.CALL_LOOKUP_ACCOUNTS, accountParams, RPC.VERSION, 1);
websocket.sendText(getAccountByName.toJsonString());
}
@Override
public void onTextFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
System.out.println("<<< "+frame.getPayloadText());
String response = frame.getPayloadText();
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){
this.mListener.onError(witnessResponse.error);
}else{
this.mListener.onSuccess(witnessResponse);
}
if(mOneTime){
websocket.disconnect();
}
}
@Override
public void onFrameSent(WebSocket websocket, WebSocketFrame frame) throws Exception {
if(frame.isTextFrame())
System.out.println(">>> "+frame.getPayloadText());
}
}

View file

@ -1,95 +0,0 @@
package cy.agorise.graphenej.api;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.neovisionaries.ws.client.WebSocket;
import com.neovisionaries.ws.client.WebSocketFrame;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import cy.agorise.graphenej.Asset;
import cy.agorise.graphenej.RPC;
import cy.agorise.graphenej.interfaces.WitnessResponseListener;
import cy.agorise.graphenej.models.ApiCall;
import cy.agorise.graphenej.models.WitnessResponse;
/**
* Class that implements lookup_asset_symbols request handler.
*
* Get the assets corresponding to the provided IDs.
*
* The response returns the assets corresponding to the provided symbols or IDs.
*
* @see <a href="https://goo.gl/WvREGV">lookup_asset_symbols API doc</a>
*/
public class LookupAssetSymbols extends BaseGrapheneHandler {
private WitnessResponseListener mListener;
private List<Asset> assets;
private boolean mOneTime;
/**
* Default Constructor
*
* @param assets list of the assets to retrieve
* @param oneTime boolean value indicating if WebSocket must be closed (true) or not
* (false) after the response
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public LookupAssetSymbols(List<Asset> assets, boolean oneTime, WitnessResponseListener listener){
super(listener);
this.assets = assets;
this.mOneTime = oneTime;
this.mListener = listener;
}
/**
* Using this constructor the WebSocket connection closes after the response.
*
* @param assets list of the assets to retrieve
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public LookupAssetSymbols(List<Asset> assets, WitnessResponseListener listener){
this(assets, true, listener);
}
@Override
public void onConnected(WebSocket websocket, Map<String, List<String>> headers) throws Exception {
ArrayList<Serializable> params = new ArrayList<>();
ArrayList<String> subArray = new ArrayList<>();
for(Asset asset : this.assets){
subArray.add(asset.getObjectId());
params.add(subArray);
}
ApiCall loginCall = new ApiCall(0, RPC.CALL_LOOKUP_ASSET_SYMBOLS, params, RPC.VERSION, 0);
websocket.sendText(loginCall.toJsonString());
}
@Override
public void onTextFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
String response = frame.getPayloadText();
System.out.println("<<< "+response);
GsonBuilder gsonBuilder = new GsonBuilder();
Type LookupAssetSymbolsResponse = new TypeToken<WitnessResponse<List<Asset>>>(){}.getType();
gsonBuilder.registerTypeAdapter(Asset.class, new Asset.AssetDeserializer());
WitnessResponse<List<Asset>> witnessResponse = gsonBuilder.create().fromJson(response, LookupAssetSymbolsResponse);
mListener.onSuccess(witnessResponse);
if(mOneTime){
websocket.disconnect();
}
}
@Override
public void onFrameSent(WebSocket websocket, WebSocketFrame frame) throws Exception {
if(frame.isTextFrame())
System.out.println(">>> "+frame.getPayloadText());
}
}

View file

@ -1,284 +0,0 @@
package cy.agorise.graphenej.api;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.neovisionaries.ws.client.WebSocket;
import com.neovisionaries.ws.client.WebSocketFrame;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import cy.agorise.graphenej.AssetAmount;
import cy.agorise.graphenej.RPC;
import cy.agorise.graphenej.Transaction;
import cy.agorise.graphenej.UserAccount;
import cy.agorise.graphenej.errors.RepeatedRequestIdException;
import cy.agorise.graphenej.interfaces.NodeErrorListener;
import cy.agorise.graphenej.interfaces.SubscriptionHub;
import cy.agorise.graphenej.interfaces.SubscriptionListener;
import cy.agorise.graphenej.interfaces.WitnessResponseListener;
import cy.agorise.graphenej.models.ApiCall;
import cy.agorise.graphenej.models.DynamicGlobalProperties;
import cy.agorise.graphenej.models.SubscriptionResponse;
import cy.agorise.graphenej.models.WitnessResponse;
import cy.agorise.graphenej.objects.Memo;
import cy.agorise.graphenej.operations.LimitOrderCreateOperation;
import cy.agorise.graphenej.operations.TransferOperation;
/**
* A WebSocket adapter prepared to be used as a basic dispatch hub for subscription messages.
*/
public class SubscriptionMessagesHub extends BaseGrapheneHandler implements SubscriptionHub {
private WebSocket mWebsocket;
// Sequence of message ids
public final static int LOGIN_ID = 1;
public final static int GET_DATABASE_ID = 2;
public final static int SUBSCRIPTION_REQUEST = 3;
// ID of subscription notifications
public final static int SUBSCRIPTION_NOTIFICATION = 4;
/**
* Id attributed to the indivitual 'get_objects' API call required for a fine-grained
* subscription request.
*/
public final static int MANUAL_SUBSCRIPTION_ID = 5;
private SubscriptionResponse.SubscriptionResponseDeserializer mSubscriptionDeserializer;
private Gson gson;
private String user;
private String password;
private boolean clearFilter;
private int currentId;
private int databaseApiId = -1;
private int subscriptionCounter = 0;
private HashMap<Long, BaseGrapheneHandler> mHandlerMap = new HashMap<>();
// State variables
private boolean isUnsubscribing;
private boolean isSubscribed;
/**
* Constructor used to create a subscription message hub that will call the set_subscribe_callback
* API with the clear_filter parameter set to false, meaning that it will only receive automatic updates
* from objects we register.
*
* A list of ObjectTypes must be provided, otherwise we won't get any update.
*
* @param user User name, in case the node to which we're going to connect to requires
* authentication
* @param password Password, same as above
* @param clearFilter Whether to automatically subscribe of not to the notification feed.
* @param errorListener Callback that will be fired in case there is an error.
*/
public SubscriptionMessagesHub(String user, String password, boolean clearFilter, NodeErrorListener errorListener){
super(errorListener);
this.user = user;
this.password = password;
this.clearFilter = clearFilter;
this.mSubscriptionDeserializer = new SubscriptionResponse.SubscriptionResponseDeserializer();
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(SubscriptionResponse.class, mSubscriptionDeserializer);
builder.registerTypeAdapter(Transaction.class, new Transaction.TransactionDeserializer());
builder.registerTypeAdapter(TransferOperation.class, new TransferOperation.TransferDeserializer());
builder.registerTypeAdapter(LimitOrderCreateOperation.class, new LimitOrderCreateOperation.LimitOrderCreateDeserializer());
builder.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer());
builder.registerTypeAdapter(UserAccount.class, new UserAccount.UserAccountSimpleDeserializer());
builder.registerTypeAdapter(DynamicGlobalProperties.class, new DynamicGlobalProperties.DynamicGlobalPropertiesDeserializer());
builder.registerTypeAdapter(Memo.class, new Memo.MemoDeserializer());
this.gson = builder.create();
}
/**
* Constructor used to create a subscription message hub that will call the
* set_subscribe_callback API with the clear_filter parameter set to false, meaning that it will
* only receive updates from objects we register.
*
* @param user User name, in case the node to which we're going to connect to requires
* authentication
* @param password Password, same as above
* @param errorListener Callback that will be fired in case there is an error.
*/
public SubscriptionMessagesHub(String user, String password, NodeErrorListener errorListener){
this(user, password, false, errorListener);
}
@Override
public void addSubscriptionListener(SubscriptionListener listener){
this.mSubscriptionDeserializer.addSubscriptionListener(listener);
}
@Override
public void removeSubscriptionListener(SubscriptionListener listener){
this.mSubscriptionDeserializer.removeSubscriptionListener(listener);
}
@Override
public List<SubscriptionListener> getSubscriptionListeners() {
return this.mSubscriptionDeserializer.getSubscriptionListeners();
}
@Override
public void onConnected(WebSocket websocket, Map<String, List<String>> headers) throws Exception {
this.mWebsocket = websocket;
ArrayList<Serializable> loginParams = new ArrayList<>();
currentId = LOGIN_ID;
loginParams.add(user);
loginParams.add(password);
ApiCall loginCall = new ApiCall(1, RPC.CALL_LOGIN, loginParams, RPC.VERSION, currentId);
websocket.sendText(loginCall.toJsonString());
}
@Override
public void onTextFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
String message = frame.getPayloadText();
System.out.println("<< "+message);
if(currentId == LOGIN_ID){
ArrayList<Serializable> emptyParams = new ArrayList<>();
ApiCall getDatabaseId = new ApiCall(1, RPC.CALL_DATABASE, emptyParams, RPC.VERSION, currentId);
websocket.sendText(getDatabaseId.toJsonString());
currentId++;
}else if(currentId == GET_DATABASE_ID){
Type ApiIdResponse = new TypeToken<WitnessResponse<Integer>>() {}.getType();
WitnessResponse<Integer> witnessResponse = gson.fromJson(message, ApiIdResponse);
databaseApiId = witnessResponse.result;
subscribe();
} else if(currentId == SUBSCRIPTION_REQUEST){
List<SubscriptionListener> subscriptionListeners = mSubscriptionDeserializer.getSubscriptionListeners();
if(!isUnsubscribing){
isSubscribed = true;
}
// If we haven't subscribed to all requested subscription channels yet,
// just send one more subscription
if(subscriptionListeners != null &&
subscriptionListeners.size() > 0 &&
subscriptionCounter < subscriptionListeners.size()){
ArrayList<Serializable> objects = new ArrayList<>();
ArrayList<Serializable> payload = new ArrayList<>();
for(SubscriptionListener listener : subscriptionListeners){
objects.add(listener.getInterestObjectType().getGenericObjectId());
}
payload.add(objects);
ApiCall subscribe = new ApiCall(databaseApiId, RPC.GET_OBJECTS, payload, RPC.VERSION, MANUAL_SUBSCRIPTION_ID);
websocket.sendText(subscribe.toJsonString());
subscriptionCounter++;
}else{
WitnessResponse witnessResponse = gson.fromJson(message, WitnessResponse.class);
if(witnessResponse.result != null &&
mHandlerMap.get(witnessResponse.id) != null){
// This is the response to a request that was submitted to the message hub
// and whose handler was stored in the "request id" -> "handler" map
BaseGrapheneHandler handler = mHandlerMap.get(witnessResponse.id);
handler.onTextFrame(websocket, frame);
mHandlerMap.remove(witnessResponse.id);
}else{
// If we've already subscribed to all requested subscription channels, we
// just proceed to deserialize content.
// The deserialization is handled by all those TypeAdapters registered in the class
// constructor while building the gson instance.
SubscriptionResponse response = gson.fromJson(message, SubscriptionResponse.class);
}
}
}
}
@Override
public void onFrameSent(WebSocket websocket, WebSocketFrame frame) throws Exception {
System.out.println(">> "+frame.getPayloadText());
}
/**
* Private method that sends a subscription request to the full node
*/
private void subscribe(){
isUnsubscribing = false;
ArrayList<Serializable> subscriptionParams = new ArrayList<>();
subscriptionParams.add(String.format("%d", SUBSCRIPTION_NOTIFICATION));
subscriptionParams.add(clearFilter);
ApiCall getDatabaseId = new ApiCall(databaseApiId, RPC.CALL_SET_SUBSCRIBE_CALLBACK, subscriptionParams, RPC.VERSION, SUBSCRIPTION_REQUEST);
mWebsocket.sendText(getDatabaseId.toJsonString());
currentId = SUBSCRIPTION_REQUEST;
}
/**
* Public method used to re-establish a subscription after it was cancelled by a previous
* call to the {@see #cancelSubscriptions()} method call.
*
* Please note that you should repeat the registration step for every interested listener, since
* those were probably lost after the previous {@see #cancelSubscriptions()} method call.
*/
public void resubscribe(){
if(mWebsocket.isOpen()){
subscribe();
}else{
throw new IllegalStateException("Websocket is not open, can't resubscribe");
}
}
/**
* Method that sends a subscription cancellation request to the full node, and also
* de-registers all subscription and request listeners.
*/
public void cancelSubscriptions(){
isSubscribed = false;
isUnsubscribing = true;
ApiCall unsubscribe = new ApiCall(databaseApiId, RPC.CALL_CANCEL_ALL_SUBSCRIPTIONS, new ArrayList<Serializable>(), RPC.VERSION, SUBSCRIPTION_REQUEST);
mWebsocket.sendText(unsubscribe.toJsonString());
// Clearing all subscription listeners
mSubscriptionDeserializer.clearAllSubscriptionListeners();
// Clearing all request handler listners
mHandlerMap.clear();
}
/**
* Method used to check the current state of the connection.
*
* @return True if the websocket is open and there is an active subscription, false otherwise.
*/
public boolean isSubscribed(){
return this.mWebsocket.isOpen() && isSubscribed;
}
/**
* Method used to reset all internal variables.
*/
public void reset(){
currentId = 0;
databaseApiId = -1;
subscriptionCounter = 0;
}
public void addRequestHandler(BaseGrapheneHandler handler) throws RepeatedRequestIdException {
if(mHandlerMap.get(handler.getRequestId()) != null){
throw new RepeatedRequestIdException("Already registered handler with id: "+handler.getRequestId());
}
mHandlerMap.put(handler.getRequestId(), handler);
try {
// Artificially calling the 'onConnected' method of the handler.
// The underlying websocket was already connected, but from the WebSocketAdapter
// point of view it doesn't make a difference.
handler.onConnected(mWebsocket, null);
} catch (Exception e) {
System.out.println("Exception. Msg: "+e.getMessage());
System.out.println("Exception type: "+e);
}
}
}

View file

@ -1,197 +0,0 @@
package cy.agorise.graphenej.api;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.neovisionaries.ws.client.WebSocket;
import com.neovisionaries.ws.client.WebSocketException;
import com.neovisionaries.ws.client.WebSocketFrame;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import cy.agorise.graphenej.Asset;
import cy.agorise.graphenej.AssetAmount;
import cy.agorise.graphenej.BlockData;
import cy.agorise.graphenej.RPC;
import cy.agorise.graphenej.Transaction;
import cy.agorise.graphenej.interfaces.WitnessResponseListener;
import cy.agorise.graphenej.models.ApiCall;
import cy.agorise.graphenej.models.BaseResponse;
import cy.agorise.graphenej.models.DynamicGlobalProperties;
import cy.agorise.graphenej.models.WitnessResponse;
/**
* Class that will handle the transaction publication procedure.
*/
public class TransactionBroadcastSequence extends BaseGrapheneHandler {
private final String TAG = this.getClass().getName();
private final static int LOGIN_ID = 1;
private final static int GET_NETWORK_BROADCAST_ID = 2;
private final static int GET_NETWORK_DYNAMIC_PARAMETERS = 3;
private final static int GET_REQUIRED_FEES = 4;
private final static int BROADCAST_TRANSACTION = 5;
private Asset feeAsset;
private Transaction transaction;
private WitnessResponseListener mListener;
private int currentId = 1;
private int broadcastApiId = -1;
private boolean mOneTime;
/**
* Default Constructor
*
* @param transaction transaction to be broadcasted.
* @param oneTime boolean value indicating if WebSocket must be closed (true) or not
* (false) after the response
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public TransactionBroadcastSequence(Transaction transaction, Asset feeAsset, boolean oneTime, WitnessResponseListener listener){
super(listener);
this.transaction = transaction;
this.feeAsset = feeAsset;
this.mOneTime = oneTime;
this.mListener = listener;
}
/**
* Using this constructor the WebSocket connection closes after the response.
*
* @param transaction: transaction to be broadcasted.
* @param listener A class implementing the WitnessResponseListener interface. This should
* be implemented by the party interested in being notified about the
* success/failure of the operation.
*/
public TransactionBroadcastSequence(Transaction transaction, Asset feeAsset, WitnessResponseListener listener){
this(transaction, feeAsset, true, listener);
}
@Override
public void onConnected(WebSocket websocket, Map<String, List<String>> headers) throws Exception {
ArrayList<Serializable> loginParams = new ArrayList<>();
loginParams.add(null);
loginParams.add(null);
ApiCall loginCall = new ApiCall(1, RPC.CALL_LOGIN, loginParams, RPC.VERSION, currentId);
websocket.sendText(loginCall.toJsonString());
}
@Override
public void onTextFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
if(frame.isTextFrame())
System.out.println("<<< "+frame.getPayloadText());
String response = frame.getPayloadText();
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(DynamicGlobalProperties.class, new DynamicGlobalProperties.DynamicGlobalPropertiesDeserializer());
Gson gson = builder.create();
BaseResponse baseResponse = gson.fromJson(response, BaseResponse.class);
if(baseResponse.error != null){
mListener.onError(baseResponse.error);
if(mOneTime){
websocket.disconnect();
}
}else{
currentId++;
ArrayList<Serializable> emptyParams = new ArrayList<>();
if(baseResponse.id == LOGIN_ID){
ApiCall networkApiIdCall = new ApiCall(1, RPC.CALL_NETWORK_BROADCAST, emptyParams, RPC.VERSION, currentId);
websocket.sendText(networkApiIdCall.toJsonString());
}else if(baseResponse.id == GET_NETWORK_BROADCAST_ID){
Type ApiIdResponse = new TypeToken<WitnessResponse<Integer>>() {}.getType();
WitnessResponse<Integer> witnessResponse = gson.fromJson(response, ApiIdResponse);
broadcastApiId = witnessResponse.result;
// 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());
}else if(baseResponse.id == GET_NETWORK_DYNAMIC_PARAMETERS){
Type DynamicGlobalPropertiesResponse = new TypeToken<WitnessResponse<DynamicGlobalProperties>>(){}.getType();
WitnessResponse<DynamicGlobalProperties> witnessResponse = gson.fromJson(response, DynamicGlobalPropertiesResponse);
DynamicGlobalProperties dynamicProperties = witnessResponse.result;
// Adjusting dynamic block data to every transaction
long expirationTime = (dynamicProperties.time.getTime() / 1000) + Transaction.DEFAULT_EXPIRATION_TIME;
String headBlockId = dynamicProperties.head_block_id;
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.AssetAmountDeserializer());
WitnessResponse<List<AssetAmount>> requiredFeesResponse = gsonBuilder.create().fromJson(response, GetRequiredFeesResponse);
// Setting fees
transaction.setFees(requiredFeesResponse.result);
ArrayList<Serializable> transactions = new ArrayList<>();
transactions.add(transaction);
ApiCall call = new ApiCall(broadcastApiId,
RPC.CALL_BROADCAST_TRANSACTION,
transactions,
RPC.VERSION,
currentId);
// Finally broadcasting transaction
websocket.sendText(call.toJsonString());
}else if(baseResponse.id >= BROADCAST_TRANSACTION){
Type WitnessResponseType = new TypeToken<WitnessResponse<String>>(){}.getType();
WitnessResponse<String> witnessResponse = gson.fromJson(response, WitnessResponseType);
mListener.onSuccess(witnessResponse);
if(mOneTime){
websocket.disconnect();
}
}
}
}
@Override
public void onFrameSent(WebSocket websocket, WebSocketFrame frame) throws Exception {
if(frame.isTextFrame()){
System.out.println(">>> "+frame.getPayloadText());
}
}
@Override
public void onError(WebSocket websocket, WebSocketException cause) throws Exception {
System.out.println("onError. cause: "+cause.getMessage());
mListener.onError(new BaseResponse.Error(cause.getMessage()));
if(mOneTime){
websocket.disconnect();
}
}
@Override
public void handleCallbackError(WebSocket websocket, Throwable cause) throws Exception {
System.out.println("handleCallbackError. cause: "+cause.getMessage()+", error: "+cause.getClass());
for (StackTraceElement element : cause.getStackTrace()){
System.out.println(element.getFileName()+"#"+element.getClassName()+":"+element.getLineNumber());
}
mListener.onError(new BaseResponse.Error(cause.getMessage()));
if(mOneTime){
websocket.disconnect();
}
}
}

View file

@ -1,143 +0,0 @@
package cy.agorise.graphenej.api.android;
import java.util.ArrayList;
import java.util.List;
import cy.agorise.graphenej.api.BaseGrapheneHandler;
import cy.agorise.graphenej.api.SubscriptionMessagesHub;
import cy.agorise.graphenej.errors.RepeatedRequestIdException;
import cy.agorise.graphenej.interfaces.NodeErrorListener;
import cy.agorise.graphenej.interfaces.WitnessResponseListener;
import cy.agorise.graphenej.models.BaseResponse;
/**
* Class used to encapsulate all connections that should be done to a node (with node hop support).
*
* This class is intended to be used as a central broker for all full node API requests. It should
* be used as a singleton under an application.
*/
public class NodeConnection {
/**
* List of URLs of the nodes
*/
private List<String> mUrlList;
/**
* Index of the current node from the list
*/
private int mUrlIndex;
private WebsocketWorkerThread mThread;
private SubscriptionMessagesHub mMessagesHub;
private long requestCounter = SubscriptionMessagesHub.MANUAL_SUBSCRIPTION_ID + 1;
private WitnessResponseListener mErrorListener;
private static NodeConnection instance;
private String mUser;
private String mPassword;
private boolean mSubscribe;
/*
* Get the instance of the NodeConnection which is intended to be used as a Singleton.
*/
public static NodeConnection getInstance(){
if(instance == null){
instance = new NodeConnection();
}
return instance;
}
public NodeConnection(){
this.mUrlList = new ArrayList<>();
}
/**
* Add a WebSocket URL node that will be added to the list used at node hop scheme.
*
* @param url: URL of the node
*/
public void addNodeUrl(String url){
System.out.println("addNodeUrl: "+url);
this.mUrlList.add(url);
}
/**
* Add a list of WebSocket URL nodes that will be added to the current list and
* be used at node hop scheme.
*
* @param urlList: List of URLs of the nodes
*/
public void addNodeUrls(List<String> urlList){
List<String> newList = new ArrayList<String>(mUrlList);
newList.addAll(urlList);
}
/**
* Get the list of WebSocket URL nodes.
*
* @return List of URLs of the nodes
*/
public List<String> getNodeUrls(){
return this.mUrlList;
}
/**
* Clear list of WebSocket URL nodes.
*/
public void clearNodeList(){
this.mUrlList.clear();
}
private NodeErrorListener mInternalErrorListener = new NodeErrorListener() {
@Override
public void onError(BaseResponse.Error error) {
System.out.println("NodeConnect Error. Msg: "+error);
connect(mUser, mPassword, mSubscribe, mErrorListener);
}
};
/**
*/
/**
* Method that will try to connect to one of the nodes. If the connection fails
* a subsequent call to this method will try to connect with the next node in the
* list if there is one.
*
* @param user user credential used for restricted requested that needed to be
* logged
* @param password password credential used for restricted requested that needed to be
* logged
* @param subscribe if the node should be subscribed to the node
* @param errorListener a class implementing the WitnessResponseListener interface. This
* should be implemented by the party interested in being notified
* about the failure of the desired broadcast operation.
*/
public void connect(String user, String password, boolean subscribe, WitnessResponseListener errorListener) {
if(this.mUrlList.size() > 0){
mUser = user;
mPassword = password;
mSubscribe = subscribe;
System.out.println("Connecting to: "+ this.mUrlList.get(mUrlIndex));
mErrorListener = errorListener;
mThread = new WebsocketWorkerThread(this.mUrlList.get(mUrlIndex), mInternalErrorListener);
mUrlIndex = mUrlIndex + 1 % this.mUrlList.size();
mMessagesHub = new SubscriptionMessagesHub(user, password, subscribe, mInternalErrorListener);
mThread.addListener(mMessagesHub);
mThread.start();
}
}
/**
* Add the API Handler to the node.
*
* @param handler request handler to be added to the connection
* @throws RepeatedRequestIdException
*/
public void addRequestHandler(BaseGrapheneHandler handler) throws RepeatedRequestIdException {
handler.setRequestId(requestCounter);
requestCounter++;
mMessagesHub.addRequestHandler(handler);
}
}

View file

@ -1,110 +0,0 @@
package cy.agorise.graphenej.api.android;
import com.neovisionaries.ws.client.WebSocket;
import com.neovisionaries.ws.client.WebSocketException;
import com.neovisionaries.ws.client.WebSocketFactory;
import com.neovisionaries.ws.client.WebSocketListener;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import javax.net.ssl.SSLContext;
import cy.agorise.graphenej.interfaces.NodeErrorListener;
import cy.agorise.graphenej.models.BaseResponse;
import cy.agorise.graphenej.test.NaiveSSLContext;
/**
* Class used to encapsulate the thread where the WebSocket does the requests.
*
*/
public class WebsocketWorkerThread extends Thread {
private final String TAG = this.getClass().getName();
// When debugging we'll use a NaiveSSLContext
public static final boolean DEBUG = true;
private final int TIMEOUT = 5000;
private WebSocket mWebSocket;
private NodeErrorListener mErrorListener;
/**
* Constructor
*
* @param url URL of the WebSocket
*/
public WebsocketWorkerThread(String url){
try {
WebSocketFactory factory = new WebSocketFactory().setConnectionTimeout(TIMEOUT);
if(DEBUG){
SSLContext context = NaiveSSLContext.getInstance("TLS");
// Set the custom SSL context.
factory.setSSLContext(context);
}
mWebSocket = factory.createSocket(url);
} catch (IOException e) {
System.out.println("IOException. Msg: "+e.getMessage());
} catch(NullPointerException e){
System.out.println("NullPointerException at WebsocketWorkerThreas. Msg: "+e.getMessage());
} catch (NoSuchAlgorithmException e) {
System.out.println("NoSuchAlgorithmException. Msg: "+e.getMessage());
}
}
/**
* Constructor with connection error listener.
*
* @param url URL of the WebSocket
* @param errorListener a class implementing the NodeErrorListener interface. This
* should be implemented by the party interested in being notified
* about the failure of the connection.
*/
public WebsocketWorkerThread(String url, NodeErrorListener errorListener){
try {
WebSocketFactory factory = new WebSocketFactory().setConnectionTimeout(TIMEOUT);
if(DEBUG){
SSLContext context = NaiveSSLContext.getInstance("TLS");
// Set the custom SSL context.
factory.setSSLContext(context);
}
mWebSocket = factory.createSocket(url);
mErrorListener = errorListener;
} catch (IOException e) {
System.out.println("IOException. Msg: "+e.getMessage());
} catch(NullPointerException e){
System.out.println("NullPointerException at WebsocketWorkerThreas. Msg: "+e.getMessage());
} catch (NoSuchAlgorithmException e) {
System.out.println("NoSuchAlgorithmException. Msg: "+e.getMessage());
}
}
/**
* Method call when the thread is started.
*/
@Override
public void run() {
try {
mWebSocket.connect();
} catch (WebSocketException e) {
System.out.println("WebSocketException. Msg: "+e.getMessage());
mErrorListener.onError(new BaseResponse.Error(e.getMessage()));
}
}
/**
* Add a WebSocketListener to the thread that will run. This should be implemented by the party
* interested in being notified about the response value of a request.
*
* @param listener listener implemented to be notified when the socket get a response from the
* node
*/
public void addListener(WebSocketListener listener){
mWebSocket.addListener(listener);
}
}

File diff suppressed because one or more lines are too long

View file

@ -1,71 +0,0 @@
/*
* Copyright 2013, 2014 Megion Research and Development GmbH
*
* Licensed under the Microsoft Reference Source License (MS-RSL)
*
* This license governs use of the accompanying software. If you use the software, you accept this license.
* If you do not accept the license, do not use the software.
*
* 1. Definitions
* The terms "reproduce," "reproduction," and "distribution" have the same meaning here as under U.S. copyright law.
* "You" means the licensee of the software.
* "Your company" means the company you worked for when you downloaded the software.
* "Reference use" means use of the software within your company as a reference, in read only form, for the sole purposes
* of debugging your products, maintaining your products, or enhancing the interoperability of your products with the
* software, and specifically excludes the right to distribute the software outside of your company.
* "Licensed patents" means any Licensor patent claims which read directly on the software as distributed by the Licensor
* under this license.
*
* 2. Grant of Rights
* (A) Copyright Grant- Subject to the terms of this license, the Licensor grants you a non-transferable, non-exclusive,
* worldwide, royalty-free copyright license to reproduce the software for reference use.
* (B) Patent Grant- Subject to the terms of this license, the Licensor grants you a non-transferable, non-exclusive,
* worldwide, royalty-free patent license under licensed patents for reference use.
*
* 3. Limitations
* (A) No Trademark License- This license does not grant you any rights to use the Licensors name, logo, or trademarks.
* (B) If you begin patent litigation against the Licensor over patents that you think may apply to the software
* (including a cross-claim or counterclaim in a lawsuit), your license to the software ends automatically.
* (C) The software is licensed "as-is." You bear the risk of using it. The Licensor gives no express warranties,
* guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot
* change. To the extent permitted under your local laws, the Licensor excludes the implied warranties of merchantability,
* fitness for a particular purpose and non-infringement.
*/
package cy.agorise.graphenej.crypto;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
public class AndroidRandomSource implements RandomSource, EntropySource {
@Override
public synchronized void nextBytes(byte[] bytes) {
// On Android we use /dev/urandom for providing random data
File file = new File("/dev/urandom");
if (!file.exists()) {
throw new RuntimeException("Unable to generate random bytes on this Android device");
}
try {
FileInputStream stream = new FileInputStream(file);
DataInputStream dis = new DataInputStream(stream);
dis.readFully(bytes);
dis.close();
} catch (IOException e) {
throw new RuntimeException("Unable to generate random bytes on this Android device", e);
}
}
@Override
public ByteBuffer provideEntropy() {
byte[] buffer = new byte[ 256 / 8];
nextBytes(buffer);
ByteBuffer byteBuffer = ByteBuffer.allocate(buffer.length);
byteBuffer.put(buffer, 0, buffer.length);
return byteBuffer;
}
}

View file

@ -1,19 +0,0 @@
package cy.agorise.graphenej.crypto;
import java.nio.ByteBuffer;
/**
* A simple interface that can be used to retrieve entropy from any source.
*
* @author owlstead
*/
public interface EntropySource {
/**
* Retrieves the entropy.
* The position of the ByteBuffer must be advanced to the limit by any users calling this method.
* The values of the bytes between the position and limit should be set to zero by any users calling this method.
*
* @return entropy within the position and limit of the given buffer
*/
ByteBuffer provideEntropy();
}

View file

@ -1,26 +0,0 @@
/*
* Copyright 2013, 2014 Megion Research & Development GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cy.agorise.graphenej.crypto;
public interface RandomSource {
/**
* Generates a user specified number of random bytes
*
* @param bytes
* The array to fill with random bytes
*/
void nextBytes(byte[] bytes);
}

View file

@ -1,15 +0,0 @@
package cy.agorise.graphenej.crypto;
import java.security.SecureRandom;
/**
* Created by nelson on 12/20/16.
*/
public class SecureRandomGenerator {
public static SecureRandom getSecureRandom(){
SecureRandomStrengthener randomStrengthener = SecureRandomStrengthener.getInstance();
// randomStrengthener.addEntropySource(new AndroidRandomSource());
return randomStrengthener.generateAndSeedRandomNumberGenerator();
}
}

View file

@ -1,162 +0,0 @@
package cy.agorise.graphenej.crypto;
import java.nio.ByteBuffer;
import java.security.DigestException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.LinkedList;
import java.util.List;
/**
* A strengthener that can be used to generate and re-seed random number
* generators that do not seed themselves appropriately.
*
* @author owlstead
*/
public class SecureRandomStrengthener {
private static final String DEFAULT_PSEUDO_RANDOM_NUMBER_GENERATOR = "SHA1PRNG";
private static final EntropySource mTimeEntropySource = new EntropySource() {
final ByteBuffer timeBuffer = ByteBuffer.allocate(Long.SIZE / Byte.SIZE
* 2);
@Override
public ByteBuffer provideEntropy() {
this.timeBuffer.clear();
this.timeBuffer.putLong(System.currentTimeMillis());
this.timeBuffer.putLong(System.nanoTime());
this.timeBuffer.flip();
return this.timeBuffer;
}
};
private final String algorithm;
private final List<EntropySource> entropySources = new LinkedList<EntropySource>();
private final MessageDigest digest;
private final ByteBuffer seedBuffer;
/**
* Generates an instance of a {@link SecureRandomStrengthener} that
* generates and re-seeds instances of {@code "SHA1PRNG"}.
*
* @return the strengthener, never null
*/
public static SecureRandomStrengthener getInstance() {
return new SecureRandomStrengthener(
DEFAULT_PSEUDO_RANDOM_NUMBER_GENERATOR);
}
/**
* Generates an instance of a {@link SecureRandomStrengthener} that
* generates instances of the given argument. Note that the availability of
* the given algorithm arguments in not tested until generation.
*
* @param algorithm
* the algorithm indicating the {@link SecureRandom} instance to
* use
* @return the strengthener, never null
*/
public static SecureRandomStrengthener getInstance(final String algorithm) {
return new SecureRandomStrengthener(algorithm);
}
private SecureRandomStrengthener(final String algorithm) {
if (algorithm == null || algorithm.length() == 0) {
throw new IllegalArgumentException(
"Please provide a PRNG algorithm string such as SHA1PRNG");
}
this.algorithm = algorithm;
try {
this.digest = MessageDigest.getInstance("SHA1");
} catch (final NoSuchAlgorithmException e) {
throw new IllegalStateException(
"MessageDigest to create seed not available", e);
}
this.seedBuffer = ByteBuffer.allocate(this.digest.getDigestLength());
}
/**
* Add an entropy source, which will be called for each generation and
* re-seeding of the given random number generator.
*
* @param source
* the source of entropy
*/
public void addEntropySource(final EntropySource source) {
if (source == null) {
throw new IllegalArgumentException(
"EntropySource should not be null");
}
this.entropySources.add(source);
}
/**
* Generates and seeds a random number generator of the configured
* algorithm. Calls the {@link EntropySource#provideEntropy()} method of all
* added sources of entropy.
*
* @return the random number generator
*/
public SecureRandom generateAndSeedRandomNumberGenerator() {
final SecureRandom secureRandom;
try {
secureRandom = SecureRandom.getInstance(this.algorithm);
} catch (final NoSuchAlgorithmException e) {
throw new IllegalStateException("PRNG is not available", e);
}
reseed(secureRandom);
return secureRandom;
}
/**
* Re-seeds the random number generator. Calls the
* {@link EntropySource#provideEntropy()} method of all added sources of
* entropy.
*
* @param secureRandom
* the random number generator to re-seed
*/
public void reseed(final SecureRandom secureRandom) {
this.seedBuffer.clear();
secureRandom.nextBytes(this.seedBuffer.array());
for (final EntropySource source : this.entropySources) {
final ByteBuffer entropy = source.provideEntropy();
if (entropy == null) {
continue;
}
final ByteBuffer wipeBuffer = entropy.duplicate();
this.digest.update(entropy);
wipe(wipeBuffer);
}
this.digest.update(mTimeEntropySource.provideEntropy());
this.digest.update(this.seedBuffer);
this.seedBuffer.clear();
// remove data from seedBuffer so it won't be retrievable
// reuse
try {
this.digest.digest(this.seedBuffer.array(), 0,
this.seedBuffer.capacity());
} catch (final DigestException e) {
throw new IllegalStateException(
"DigestException should not be thrown", e);
}
secureRandom.setSeed(this.seedBuffer.array());
wipe(this.seedBuffer);
}
private void wipe(final ByteBuffer buf) {
while (buf.hasRemaining()) {
buf.put((byte) 0);
}
}
}

View file

@ -1,10 +0,0 @@
package cy.agorise.graphenej.errors;
/**
* Created by nelson on 12/20/16.
*/
public class ChecksumException extends Exception {
public ChecksumException(String message){
super(message);
}
}

View file

@ -1,11 +0,0 @@
package cy.agorise.graphenej.errors;
/**
* Created by nelson on 1/18/17.
*/
public class IncompatibleOperation extends RuntimeException {
public IncompatibleOperation(String message){
super(message);
}
}

View file

@ -1,15 +0,0 @@
package cy.agorise.graphenej.errors;
/**
* Created by nelson on 12/25/16.
*/
public class IncompleteAssetError extends RuntimeException{
public IncompleteAssetError(String message){
super(message);
}
public IncompleteAssetError(){
super("The asset used in this method is probably incomplete, Assets instances can be created with just its id information but this context requires more information");
}
}

View file

@ -1,10 +0,0 @@
package cy.agorise.graphenej.errors;
/**
* Created by nelson on 12/1/16.
*/
public class MalformedAddressException extends Exception {
public MalformedAddressException(String message){
super(message);
}
}

View file

@ -1,11 +0,0 @@
package cy.agorise.graphenej.errors;
/**
* Created by nelson on 3/1/17.
*/
public class MalformedOperationException extends RuntimeException {
public MalformedOperationException(String msg){
super(msg);
}
}

View file

@ -1,11 +0,0 @@
package cy.agorise.graphenej.errors;
/**
* Created by nelson on 11/14/16.
*/
public class MalformedTransactionException extends Exception {
public MalformedTransactionException(String message){
super(message);
}
}

View file

@ -1,12 +0,0 @@
package cy.agorise.graphenej.errors;
/**
* Created by nelson on 6/27/17.
*/
public class RepeatedRequestIdException extends Exception {
public RepeatedRequestIdException(String message){
super(message);
}
}

View file

@ -1,9 +0,0 @@
package cy.agorise.graphenej.interfaces;
/**
* Interface implemented by all entities for which makes sense to have a byte-array representation.
*/
public interface ByteSerializable {
byte[] toBytes();
}

View file

@ -1,7 +0,0 @@
package cy.agorise.graphenej.interfaces;
/**
* Interface used to group both ByteSerializable and JsonSerializable interfaces.
*/
public interface GrapheneSerializable extends ByteSerializable, JsonSerializable {
}

View file

@ -1,16 +0,0 @@
package cy.agorise.graphenej.interfaces;
import com.google.gson.JsonElement;
import java.io.Serializable;
/**
* Interface to be implemented by any entity for which makes sense to
* have a JSON-formatted string and object representation.
*/
public interface JsonSerializable extends Serializable {
String toJsonString();
JsonElement toJsonObject();
}

View file

@ -1,10 +0,0 @@
package cy.agorise.graphenej.interfaces;
import cy.agorise.graphenej.models.BaseResponse;
/**
* Interface to be implemented by any listener to network errors.
*/
public interface NodeErrorListener {
void onError(BaseResponse.Error error);
}

View file

@ -1,30 +0,0 @@
package cy.agorise.graphenej.interfaces;
import java.util.List;
/**
* Interface to be implemented by any class that hosts a SubscriptionResponseDeserializer and wants to
* expose an interface for its management of its listeners.
*
* Created by nelson on 1/30/17.
*/
public interface SubscriptionHub {
/**
* Adds a given listener to the list of subscription listeners.
* @param listener: The SubscriptionListener to add.
*/
void addSubscriptionListener(SubscriptionListener listener);
/**
* Removes a given listener from the list.
* @param listener: The SubscriptionListener to remove.
*/
void removeSubscriptionListener(SubscriptionListener listener);
/**
* Retrieves a list of all subscription listeners.
* @return
*/
List<SubscriptionListener> getSubscriptionListeners();
}

View file

@ -1,30 +0,0 @@
package cy.agorise.graphenej.interfaces;
import cy.agorise.graphenej.ObjectType;
import cy.agorise.graphenej.models.SubscriptionResponse;
/**
* Generic interface that must be implemented by any class that wants to be informed about a specific
* event notification.
*
* Created by nelson on 1/26/17.
*/
public interface SubscriptionListener {
/**
* Every subscription listener must implement a method that returns the type of object it is
* interested in.
* @return: Instance of the ObjectType enum class.
*/
ObjectType getInterestObjectType();
/**
* Method called whenever there is an update that might be of interest for this listener.
* Note however that the objects returned inside the SubscriptionResponse are not guaranteed to be
* only of the object type requested by this class in the getInterestObjectType.
*
* @param response: SubscriptionResponse instance, which may or may not contain an object of interest.
*/
void onSubscriptionUpdate(SubscriptionResponse response);
}

View file

@ -1,14 +0,0 @@
package cy.agorise.graphenej.interfaces;
import cy.agorise.graphenej.models.BaseResponse;
import cy.agorise.graphenej.models.WitnessResponse;
/**
* Class used to represent any listener to network requests.
*/
public interface WitnessResponseListener {
void onSuccess(WitnessResponse response);
void onError(BaseResponse.Error error);
}

View file

@ -1,23 +0,0 @@
package cy.agorise.graphenej.models;
import java.io.Serializable;
import cy.agorise.graphenej.GrapheneObject;
/**
* Created by nelson on 1/12/17.
*/
public class AccountBalanceUpdate extends GrapheneObject implements Serializable {
public static final String KEY_OWNER = "owner";
public static final String KEY_ASSET_TYPE = "asset_type";
public static final String KEY_BALANCE = "balance";
public String owner;
public String asset_type;
public long balance;
public AccountBalanceUpdate(String id) {
super(id);
}
}

View file

@ -1,30 +0,0 @@
package cy.agorise.graphenej.models;
import cy.agorise.graphenej.AccountOptions;
import cy.agorise.graphenej.Authority;
/**
* Created by nelson on 11/15/16.
*/
public class AccountProperties {
public String id;
public String membership_expiration_date;
public String registrar;
public String referrer;
public String lifetime_referrer;
public long network_fee_percentage;
public long lifetime_referrer_fee_percentage;
public long referrer_rewards_percentage;
public String name;
public Authority owner;
public Authority active;
public AccountOptions options;
public String statistics;
public String[] whitelisting_accounts;
public String[] blacklisting_accounts;
public String[] whitelisted_accounts;
public String[] blacklisted_accounts;
public Object[] owner_special_authority;
public Object[] active_special_authority;
public long top_n_control_flags;
}

View file

@ -1,110 +0,0 @@
package cy.agorise.graphenej.models;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import cy.agorise.graphenej.interfaces.JsonSerializable;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
/**
* Class used to build a Graphene websocket API call.
* @see <a href="http://docs.bitshares.org/api/websocket.html">Websocket Calls & Notifications</a>
*/
public class ApiCall implements JsonSerializable {
public static final String KEY_SEQUENCE_ID = "id";
public static final String KEY_METHOD = "method";
public static final String KEY_PARAMS = "params";
public static final String KEY_JSON_RPC = "jsonrpc";
public String method;
public String methodToCall;
public String jsonrpc;
public List<Serializable> params;
public int apiId;
public int sequenceId;
public ApiCall(int apiId, String methodToCall, List<Serializable> params, String jsonrpc, int sequenceId){
this.apiId = apiId;
this.method = "call";
this.methodToCall = methodToCall;
this.jsonrpc = jsonrpc;
this.params = params;
this.sequenceId = sequenceId;
}
public ApiCall(int apiId, String method, String methodToCall, List<Serializable> params, String jsonrpc, int sequenceId){
this.apiId = apiId;
this.method = method;
this.methodToCall = methodToCall;
this.jsonrpc = jsonrpc;
this.params = params;
this.sequenceId = sequenceId;
}
@Override
public String toJsonString() {
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(ApiCall.class, new ApiCallSerializer());
return gsonBuilder.create().toJson(this);
}
@Override
public JsonElement toJsonObject() {
JsonObject obj = new JsonObject();
obj.addProperty(KEY_SEQUENCE_ID, this.sequenceId);
obj.addProperty(KEY_METHOD, this.method);
JsonArray paramsArray = new JsonArray();
paramsArray.add(this.apiId);
paramsArray.add(this.methodToCall);
JsonArray methodParams = new JsonArray();
for(int i = 0; i < this.params.size(); i++){
if(this.params.get(i) instanceof JsonSerializable) {
// Sometimes the parameters are objects
methodParams.add(((JsonSerializable) this.params.get(i)).toJsonObject());
}else if (Number.class.isInstance(this.params.get(i))){
// Other times they are numbers
methodParams.add( (Number) this.params.get(i));
}else if(this.params.get(i) instanceof String || this.params.get(i) == null){
// Other times they are plain strings
methodParams.add((String) this.params.get(i));
}else if(this.params.get(i) instanceof ArrayList) {
// Other times it might be an array
JsonArray array = new JsonArray();
ArrayList<Serializable> listArgument = (ArrayList<Serializable>) this.params.get(i);
for (int l = 0; l < listArgument.size(); l++) {
Serializable element = listArgument.get(l);
if (element instanceof JsonSerializable)
array.add(((JsonSerializable) element).toJsonObject());
else if (element instanceof String) {
array.add((String) element);
}
}
methodParams.add(array);
}else if(this.params.get(i) instanceof Boolean){
methodParams.add((boolean) this.params.get(i));
}else{
System.out.println("Skipping parameter of type: "+this.params.get(i).getClass());
}
}
paramsArray.add(methodParams);
obj.add(KEY_PARAMS, paramsArray);
obj.addProperty(KEY_JSON_RPC, this.jsonrpc);
return obj;
}
class ApiCallSerializer implements JsonSerializer<ApiCall> {
@Override
public JsonElement serialize(ApiCall apiCall, Type type, JsonSerializationContext jsonSerializationContext) {
return toJsonObject();
}
}
}

View file

@ -1,13 +0,0 @@
package cy.agorise.graphenej.models;
import cy.agorise.graphenej.Price;
/**
* Created by nelson on 1/9/17.
*/
public class AssetFeed {
public Price settlement_price;
public long maintenance_collateral_ratio;
public long maximum_short_squeeze_ratio;
public Price core_exchange_rate;
}

View file

@ -1,29 +0,0 @@
package cy.agorise.graphenej.models;
import com.google.gson.*;
import cy.agorise.graphenej.Asset;
import java.lang.reflect.Type;
/**
* Created by nelson on 1/25/17.
*/
public class AssetHolderCount {
public static final String KEY_ASSET_ID = "asset_id";
public static final String KEY_COUNT = "count";
public Asset asset;
public long count;
public static class HoldersCountDeserializer implements JsonDeserializer<AssetHolderCount> {
@Override
public AssetHolderCount deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
AssetHolderCount holdersCount = new AssetHolderCount();
holdersCount.asset = new Asset(jsonObject.get(KEY_ASSET_ID).getAsString());
holdersCount.count = jsonObject.get(KEY_COUNT).getAsLong();
return holdersCount;
}
}
}

View file

@ -1,29 +0,0 @@
package cy.agorise.graphenej.models;
/**
* Created by nelson on 11/12/16.
*/
public class BaseResponse {
public long id;
public Error error;
public static class Error {
public ErrorData data;
public int code;
public String message;
public Error(String message){
this.message = message;
}
}
public static class ErrorData {
public int code;
public String name;
public String message;
//TODO: Include stack data
public ErrorData(String message){
this.message = message;
}
}
}

View file

@ -1,25 +0,0 @@
package cy.agorise.graphenej.models;
import cy.agorise.graphenej.GrapheneObject;
import cy.agorise.graphenej.Price;
/**
* This is the representation of the response from the 'get_objects' call with
* a 2.4.x id, which will retrieve a 'impl_asset_bitasset_data_type'.
*
* Created by nelson on 1/8/17.
*/
public class BitAssetData extends GrapheneObject {
public Object[] feeds;
public AssetFeed current_feed;
public String current_feed_publication_time;
public Object options;
public long force_settled_volume;
public boolean is_prediction_market;
public Price settlement_price;
public long settlement_fund;
public BitAssetData(String id) {
super(id);
}
}

View file

@ -1,12 +0,0 @@
package cy.agorise.graphenej.models;
/**
* Created by nelson on 12/13/16.
*/
public class BlockHeader {
public String previous;
public String timestamp;
public String witness;
public String transaction_merkle_root;
public Object[] extension;
}

View file

@ -1,42 +0,0 @@
package cy.agorise.graphenej.models;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import cy.agorise.graphenej.GrapheneObject;
import cy.agorise.graphenej.Transaction;
import java.io.Serializable;
import java.lang.reflect.Type;
/**
* Created by nelson on 1/28/17.
*/
public class BroadcastedTransaction extends GrapheneObject implements Serializable {
public static final String KEY_TRX = "trx";
public static final String KEY_TRX_ID = "trx_id";
private Transaction trx;
private String trx_id;
public BroadcastedTransaction(String id){
super(id);
}
public void setTransaction(Transaction t){
this.trx = t;
}
public Transaction getTransaction() {
return trx;
}
public void setTransactionId(String id){
this.trx_id = id;
}
public String getTransactionId() {
return trx_id;
}
}

View file

@ -1,84 +0,0 @@
package cy.agorise.graphenej.models;
import com.google.gson.*;
import cy.agorise.graphenej.Asset;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Created by nelson on 12/22/16.
*/
public class BucketObject {
public static final String KEY_HIGH_BASE = "high_base";
public static final String KEY_HIGH_QUOTE = "high_quote";
public static final String KEY_LOW_BASE = "low_base";
public static final String KEY_LOW_QUOTE = "low_quote";
public static final String KEY_OPEN_BASE = "open_base";
public static final String KEY_OPEN_QUOTE = "open_quote";
public static final String KEY_CLOSE_BASE = "close_base";
public static final String KEY_CLOSE_QUOTE = "close_quote";
public static final String KEY_BASE_VOLUME = "base_volume";
public static final String KEY_QUOTE_VOLUME = "quote_volume";
public static final String KEY_BASE = "base";
public static final String KEY_QUOTE = "quote";
public static final String KEY_SECONDS = "seconds";
public static final String KEY_OPEN = "open";
public static final String KEY_KEY = "key";
public String id;
public Key key;
public BigDecimal high_base;
public BigDecimal high_quote;
public BigDecimal low_base;
public BigDecimal low_quote;
public BigDecimal open_base;
public BigDecimal open_quote;
public BigDecimal close_base;
public BigDecimal close_quote;
public BigDecimal base_volume;
public BigDecimal quote_volume;
public static class Key {
public Asset base;
public Asset quote;
public long seconds;
public Date open;
}
public static class BucketDeserializer implements JsonDeserializer<BucketObject> {
@Override
public BucketObject deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonBucket = json.getAsJsonObject();
BucketObject bucket = new BucketObject();
bucket.high_base = jsonBucket.get(KEY_HIGH_BASE).getAsBigDecimal();
bucket.high_quote = jsonBucket.get(KEY_HIGH_QUOTE).getAsBigDecimal();
bucket.low_base = jsonBucket.get(KEY_LOW_BASE).getAsBigDecimal();
bucket.low_quote = jsonBucket.get(KEY_LOW_QUOTE).getAsBigDecimal();
bucket.open_base = jsonBucket.get(KEY_OPEN_BASE).getAsBigDecimal();
bucket.open_quote = jsonBucket.get(KEY_OPEN_QUOTE).getAsBigDecimal();
bucket.close_base = jsonBucket.get(KEY_CLOSE_BASE).getAsBigDecimal();
bucket.close_quote = jsonBucket.get(KEY_CLOSE_QUOTE).getAsBigDecimal();
bucket.base_volume = jsonBucket.get(KEY_BASE_VOLUME).getAsBigDecimal();
bucket.quote_volume = jsonBucket.get(KEY_QUOTE_VOLUME).getAsBigDecimal();
bucket.key = new Key();
String baseId = jsonBucket.get(KEY_KEY).getAsJsonObject().get(KEY_BASE).getAsString();
String quoteId = jsonBucket.get(KEY_KEY).getAsJsonObject().get(KEY_QUOTE).getAsString();
bucket.key.base = new Asset(baseId);
bucket.key.quote = new Asset(quoteId);
bucket.key.seconds = jsonBucket.get(KEY_KEY).getAsJsonObject().get(KEY_SECONDS).getAsLong();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
try {
bucket.key.open = dateFormat.parse(jsonBucket.get(KEY_KEY).getAsJsonObject().get(KEY_OPEN).getAsString());
} catch (ParseException e) {
System.out.println("ParseException while deserializing BucketObject. Msg: "+e.getMessage());
}
return bucket;
}
}
}

View file

@ -1,99 +0,0 @@
package cy.agorise.graphenej.models;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import cy.agorise.graphenej.GrapheneObject;
import cy.agorise.graphenej.Util;
/**
* Class used to deserialize the 'result' field returned by the full node after making a call
* to the 'get_dynamic_global_properties' RPC.
*/
public class DynamicGlobalProperties extends GrapheneObject implements Serializable {
public static final String KEY_HEAD_BLOCK_NUMBER = "head_block_number";
public static final String KEY_HEAD_BLOCK_ID ="head_block_id";
public static final String KEY_TIME = "time";
public static final String KEY_CURRENT_WITNESS = "current_witness";
public static final String KEY_NEXT_MAINTENANCE_TIME = "next_maintenance_time";
public static final String KEY_LAST_BUDGET_TIME = "last_budget_time";
public static final String KEY_WITNESS_BUDGET = "witness_budget";
public static final String KEY_ACCOUNTS_REGISTERED_THIS_INTERVAL = "accounts_registered_this_interval";
public static final String KEY_RECENTLY_MISSED_COUNT = "recently_missed_count";
public static final String KEY_CURRENT_ASLOT = "current_aslot";
public static final String KEY_RECENT_SLOTS_FILLED = "recent_slots_filled";
public static final String KEY_DYNAMIC_FLAGS = "dynamic_flags";
public static final String KEY_LAST_IRREVERSIBLE_BLOCK_NUM = "last_irreversible_block_num";
public long head_block_number;
public String head_block_id;
public Date time;
public String current_witness;
public Date next_maintenance_time;
public String last_budget_time;
public long witness_budget;
public long accounts_registered_this_interval;
public long recently_missed_count;
public long current_aslot;
public String recent_slots_filled;
public int dynamic_flags;
public long last_irreversible_block_num;
public DynamicGlobalProperties(String id) {
super(id);
}
/**
* Class that will parse the JSON element containing the dynamic global properties object and
* return an instance of the {@link DynamicGlobalProperties} class.
*/
public static class DynamicGlobalPropertiesDeserializer implements JsonDeserializer<DynamicGlobalProperties> {
@Override
public DynamicGlobalProperties deserialize(JsonElement jsonElement, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonObject = jsonElement.getAsJsonObject();
// Creating an instance of the DynamicGlobalProperties
DynamicGlobalProperties dynamicGlobal = new DynamicGlobalProperties(jsonElement.getAsJsonObject().get(KEY_ID).getAsString());
// Start to fill in the parsed details
dynamicGlobal.head_block_number = jsonObject.get(DynamicGlobalProperties.KEY_HEAD_BLOCK_NUMBER).getAsLong();
dynamicGlobal.head_block_id = jsonObject.get(DynamicGlobalProperties.KEY_HEAD_BLOCK_ID).getAsString();
SimpleDateFormat dateFormat = new SimpleDateFormat(Util.TIME_DATE_FORMAT);
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
try {
dynamicGlobal.time = dateFormat.parse(jsonObject.get(DynamicGlobalProperties.KEY_TIME).getAsString());
} catch (ParseException e) {
System.out.println("ParseException. Msg: "+e.getMessage());
}
try {
dynamicGlobal.next_maintenance_time = dateFormat.parse(jsonObject.get(DynamicGlobalProperties.KEY_NEXT_MAINTENANCE_TIME).getAsString());
} catch (ParseException e) {
System.out.println("ParseException. Msg: "+e.getMessage());
}
dynamicGlobal.current_witness = jsonObject.get(DynamicGlobalProperties.KEY_CURRENT_WITNESS).getAsString();
dynamicGlobal.last_budget_time = jsonObject.get(DynamicGlobalProperties.KEY_LAST_BUDGET_TIME).getAsString();
dynamicGlobal.witness_budget = jsonObject.get(DynamicGlobalProperties.KEY_WITNESS_BUDGET).getAsLong();
dynamicGlobal.accounts_registered_this_interval = jsonObject.get(DynamicGlobalProperties.KEY_ACCOUNTS_REGISTERED_THIS_INTERVAL).getAsLong();
dynamicGlobal.recently_missed_count = jsonObject.get(DynamicGlobalProperties.KEY_RECENTLY_MISSED_COUNT).getAsLong();
dynamicGlobal.current_aslot = jsonObject.get(DynamicGlobalProperties.KEY_CURRENT_ASLOT).getAsLong();
dynamicGlobal.recent_slots_filled = jsonObject.get(DynamicGlobalProperties.KEY_RECENT_SLOTS_FILLED).getAsString();
dynamicGlobal.dynamic_flags = jsonObject.get(DynamicGlobalProperties.KEY_DYNAMIC_FLAGS).getAsInt();
dynamicGlobal.last_irreversible_block_num = jsonObject.get(DynamicGlobalProperties.KEY_LAST_IRREVERSIBLE_BLOCK_NUM).getAsLong();
return dynamicGlobal;
}
}
}

View file

@ -1,69 +0,0 @@
package cy.agorise.graphenej.models;
import cy.agorise.graphenej.operations.TransferOperation;
/**
* This class offers support to deserialization of transfer operations received by the API
* method get_relative_account_history.
*
* More operations types might be listed in the response of that method, but by using this class
* those will be filtered out of the parsed result.
*/
public class HistoricalTransfer {
private String id;
private TransferOperation op;
public Object[] result;
private long block_num;
private long trx_in_block;
private long op_in_trx;
private long virtual_op;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public TransferOperation getOperation() {
return op;
}
public void setOperation(TransferOperation op) {
this.op = op;
}
public long getBlockNum() {
return block_num;
}
public void setBlockNum(long block_num) {
this.block_num = block_num;
}
public long getTransactionsInBlock() {
return trx_in_block;
}
public void setTransactionsInBlock(long trx_in_block) {
this.trx_in_block = trx_in_block;
}
public long getOperationsInTrx() {
return op_in_trx;
}
public void setOperationsInTrx(long op_in_trx) {
this.op_in_trx = op_in_trx;
}
public long getVirtualOp() {
return virtual_op;
}
public void setVirtualOp(long virtual_op) {
this.virtual_op = virtual_op;
}
}

View file

@ -1,201 +0,0 @@
package cy.agorise.graphenej.models;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import cy.agorise.graphenej.GrapheneObject;
import cy.agorise.graphenej.ObjectType;
import cy.agorise.graphenej.Transaction;
import cy.agorise.graphenej.interfaces.SubscriptionListener;
/**
* Class that represents a generic subscription response.
* The template for every subscription response is the following:
*
* {
* "method": "notice"
* "params": [
* SUBSCRIPTION_ID,
* [[
* { "id": "2.1.0", ... },
* { "id": ... },
* { "id": ... },
* { "id": ... }
* ]]
* ],
* }
*
* As of 1/2017, the witness API returns all sort of events, not just the ones we're interested in once we
* make a call to the 'set_subscribe_callback', regardless of whether the 'clear_filter' parameter is set to
* true or false.
*
* To minimize CPU usage, we introduce a scheme of selective parsing, implemented by the static inner class
* SubscriptionResponseDeserializer.
*
* Created by nelson on 1/12/17.
*/
public class SubscriptionResponse {
private static final String TAG = "SubscriptionResponse";
public static final String KEY_ID = "id";
public static final String KEY_METHOD = "method";
public static final String KEY_PARAMS = "params";
public int id;
public String method;
public List<Serializable> params;
/**
* Inner static class used to parse and deserialize subscription responses in a partial way,
* depending on the amount of SubscriptionListeners we might have registered.
*
* The rationale behind these architecture is to avoid wasting computational resources parsing unneeded
* objects that might come once the are subscribed to the witness notifications.
*/
public static class SubscriptionResponseDeserializer implements JsonDeserializer<SubscriptionResponse> {
/**
* Map of ObjectType to Integer used to keep track of the current amount of listener per type
*/
private HashMap<ObjectType, Integer> listenerTypeCount;
/**
* List of listeners
*/
private LinkedList<SubscriptionListener> mListeners;
/**
* Constructor that will just create a list of SubscriptionListeners and
* a map of ObjectType to integer in order to keep track of how many listeners
* to each type of object we have.
*/
public SubscriptionResponseDeserializer(){
mListeners = new LinkedList<>();
listenerTypeCount = new HashMap<>();
}
/**
* Adds a subscription listener to the list.
* @param subscriptionListener: Class implementing the {@see SubscriptionListener} interface
* to be added to the list.
*/
public void addSubscriptionListener(SubscriptionListener subscriptionListener){
int currentCount = 0;
if(listenerTypeCount.containsKey(subscriptionListener.getInterestObjectType())){
currentCount = listenerTypeCount.get(subscriptionListener.getInterestObjectType());
}
this.listenerTypeCount.put(subscriptionListener.getInterestObjectType(), currentCount + 1);
this.mListeners.add(subscriptionListener);
}
/**
* Retrieves the full list of SubscriptionListeners registered.
* @return
*/
public List<SubscriptionListener> getSubscriptionListeners(){
return this.mListeners;
}
/**
* Removes a subscription listener to the list.
* @param subscriptionListener: Class implementing the {@see SubscriptionListener} interface
* to be removed from the list.
*/
public void removeSubscriptionListener(SubscriptionListener subscriptionListener){
int currentCount = listenerTypeCount.get(subscriptionListener.getInterestObjectType());
if(currentCount != 0){
this.listenerTypeCount.put(subscriptionListener.getInterestObjectType(), currentCount);
}else{
System.out.println("Trying to remove subscription listener, but none is registered!");
}
this.mListeners.remove(subscriptionListener);
}
/**
* Removes all registered subscription listeners
*/
public void clearAllSubscriptionListeners(){
this.mListeners.clear();
this.listenerTypeCount.clear();
}
@Override
public SubscriptionResponse deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
SubscriptionResponse response = new SubscriptionResponse();
JsonObject responseObject = json.getAsJsonObject();
if(!responseObject.has(KEY_METHOD)){
return response;
}
response.method = responseObject.get(KEY_METHOD).getAsString();
JsonArray paramsArray = responseObject.get(KEY_PARAMS).getAsJsonArray();
response.params = new ArrayList<>();
response.params.add(paramsArray.get(0).getAsInt());
ArrayList<Serializable> secondArgument = new ArrayList<>();
response.params.add(secondArgument);
// Hash map used to record the type of objects present in this subscription message
// and only alert listeners that might be interested
HashMap<ObjectType, Boolean> objectMap = new HashMap<>();
JsonArray subArray = paramsArray.get(1).getAsJsonArray().get(0).getAsJsonArray();
for(JsonElement object : subArray){
if(object.isJsonObject()){
GrapheneObject grapheneObject = new GrapheneObject(object.getAsJsonObject().get(KEY_ID).getAsString());
int listenerTypeCount = 0;
if(this.listenerTypeCount.containsKey(grapheneObject.getObjectType())){
listenerTypeCount = this.listenerTypeCount.get(grapheneObject.getObjectType());
}
/*
* Here's where we apply the selective deserialization logic, meaning we only completely deserialize
* an object contained in a notification if there is at least one registered listener interested in
* objects of that type.
*/
if(listenerTypeCount > 0){
JsonObject jsonObject = object.getAsJsonObject();
if(grapheneObject.getObjectType() == ObjectType.ACCOUNT_BALANCE_OBJECT){
AccountBalanceUpdate balanceObject = new AccountBalanceUpdate(grapheneObject.getObjectId());
balanceObject.owner = jsonObject.get(AccountBalanceUpdate.KEY_OWNER).getAsString();
balanceObject.asset_type = jsonObject.get(AccountBalanceUpdate.KEY_ASSET_TYPE).getAsString();
balanceObject.balance = jsonObject.get(AccountBalanceUpdate.KEY_BALANCE).getAsLong();
objectMap.put(ObjectType.ACCOUNT_BALANCE_OBJECT, true);
secondArgument.add(balanceObject);
}else if(grapheneObject.getObjectType() == ObjectType.DYNAMIC_GLOBAL_PROPERTY_OBJECT){
DynamicGlobalProperties dynamicGlobalProperties = context.deserialize(object, DynamicGlobalProperties.class);
objectMap.put(ObjectType.DYNAMIC_GLOBAL_PROPERTY_OBJECT, true);
secondArgument.add(dynamicGlobalProperties);
}else if(grapheneObject.getObjectType() == ObjectType.TRANSACTION_OBJECT){
BroadcastedTransaction broadcastedTransaction = new BroadcastedTransaction(grapheneObject.getObjectId());
broadcastedTransaction.setTransaction((Transaction) context.deserialize(jsonObject.get(BroadcastedTransaction.KEY_TRX), Transaction.class));
broadcastedTransaction.setTransactionId(jsonObject.get(BroadcastedTransaction.KEY_TRX_ID).getAsString());
objectMap.put(ObjectType.TRANSACTION_OBJECT, true);
secondArgument.add(broadcastedTransaction);
}else{
//TODO: Add support for other types of objects
}
}
}else{
secondArgument.add(object.getAsString());
}
}
for(SubscriptionListener listener : mListeners){
// Only notify the listener if there is an object of interest in
// this notification
if(objectMap.containsKey(listener.getInterestObjectType())){
listener.onSubscriptionUpdate(response);
}
}
return response;
}
}
}

View file

@ -1,11 +0,0 @@
package cy.agorise.graphenej.models;
/**
* Generic witness response
*/
public class WitnessResponse<T> extends BaseResponse{
public static final String KEY_ID = "id";
public static final String KEY_RESULT = "result";
public T result;
}

View file

@ -1,31 +0,0 @@
package cy.agorise.graphenej.models.backup;
/**
* Class used to represent an entry in the "linked_accounts" field of the JSON-formatted backup file.
* Created by nelson on 2/15/17.
*/
public class LinkedAccount {
private String name;
private String chainId;
public LinkedAccount(String name, String chainId){
this.name = name;
this.chainId = chainId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getChainId() {
return chainId;
}
public void setChainId(String chainId) {
this.chainId = chainId;
}
}

View file

@ -1,38 +0,0 @@
package cy.agorise.graphenej.models.backup;
import cy.agorise.graphenej.Address;
import cy.agorise.graphenej.Util;
import org.bitcoinj.core.ECKey;
/**
* Class used to represent an entry in the "private_keys" array field in the JSON-formatted
* backup file.
*
* Created by nelson on 2/14/17.
*/
public class PrivateKeyBackup {
public String encrypted_key;
public String pubkey;
public int brainkey_sequence;
public int id;
public PrivateKeyBackup(byte[] privateKey, int brainkeySequence, int id, byte[] encryptionKey){
this.encrypted_key = encryptPrivateKey(privateKey, encryptionKey);
this.brainkey_sequence = brainkeySequence;
this.id = id;
deriveAddress(privateKey);
}
public byte[] decryptPrivateKey(byte[] encryptionKey){
return Util.decryptAES(Util.hexToBytes(encrypted_key), encryptionKey);
}
public String encryptPrivateKey(byte[] data, byte[] encryptionKey){
return Util.bytesToHex(Util.encryptAES(data, encryptionKey));
}
private void deriveAddress(byte[] privateKey){
Address address = new Address(ECKey.fromPublicOnly(ECKey.fromPrivate(privateKey).getPubKey()));
this.pubkey = address.toString();
}
}

View file

@ -1,202 +0,0 @@
package cy.agorise.graphenej.models.backup;
import cy.agorise.graphenej.Address;
import cy.agorise.graphenej.Util;
import cy.agorise.graphenej.crypto.SecureRandomGenerator;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.Sha256Hash;
import org.spongycastle.crypto.digests.SHA256Digest;
import java.io.UnsupportedEncodingException;
import java.security.SecureRandom;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* This class holds data deserialized from a wallet in the backup file.
*
* Created by nelson on 2/14/17.
*/
public class Wallet {
private String public_name;
private String password_pubkey;
private String encryption_key;
private String encrypted_brainkey;
private String brainkey_pubkey;
private int brainkey_sequence;
private String brainkey_backup_date;
private String created;
private String last_modified;
private String chain_id;
private String id;
private String backup_date;
/**
* No args constructor
*/
public Wallet(){}
public Wallet(String name){
this.public_name = name;
this.id = name;
}
/**
* Wallet constructor that takes a few arguments.
* @param name: The name of this wallet.
* @param brainKey: The brain key to be used.
* @param brainkeySequence: The brain key sequence.
* @param chainId: The chain id
* @param password: Password used to encrypt all sensitive data.
*/
public Wallet(String name, String brainKey, int brainkeySequence, String chainId, String password){
this(name);
SecureRandom secureRandom = SecureRandomGenerator.getSecureRandom();
byte[] decryptedKey = new byte[Util.KEY_LENGTH];
secureRandom.nextBytes(decryptedKey);
this.encryption_key = Util.bytesToHex(Util.encryptAES(decryptedKey, password.getBytes()));
this.encrypted_brainkey = Util.bytesToHex(Util.encryptAES(brainKey.getBytes(), decryptedKey));
this.brainkey_sequence = brainkeySequence;
this.chain_id = chainId;
try {
byte[] passwordHash = Sha256Hash.hash(password.getBytes("UTF8"));
this.password_pubkey = new Address(ECKey.fromPublicOnly(ECKey.fromPrivate(passwordHash).getPubKey())).toString();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
try{
byte[] brainkeyHash = Sha256Hash.hash(brainKey.getBytes("UTF8"));
this.brainkey_pubkey = new Address(ECKey.fromPublicOnly(ECKey.fromPrivate(brainkeyHash).getPubKey())).toString();
} catch(UnsupportedEncodingException e){
e.printStackTrace();
}
Date now = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat(Util.TIME_DATE_FORMAT);
this.created = dateFormat.format(now);
this.last_modified = created;
this.backup_date = created;
this.brainkey_backup_date = created;
}
/**
* Method that will return the decrypted version of the "encrypted_brainkey" field.
* @param password: Password used to encrypt the encryption key.
* @return: The brainkey in its plaintext version.
*/
public String decryptBrainKey(String password){
byte[] decryptedKey = getEncryptionKey(password);
byte[] encryptedBrainKey = Util.hexToBytes(encrypted_brainkey);
return new String(Util.decryptAES(encryptedBrainKey, decryptedKey));
}
/**
* Decrypts the encryption key, which is also provided in an encrypted form.
* @param password: The password used to encrypt the encryption key.
* @return: The encryption key.
*/
public byte[] getEncryptionKey(String password){
return Util.decryptAES(Util.hexToBytes(encryption_key), password.getBytes());
}
public String getPrivateName() {
return public_name;
}
public void setPrivateName(String privateName) {
this.public_name = privateName;
}
public String getPasswordPubkey() {
return password_pubkey;
}
public void setPasswordPubkey(String password_pubkey) {
this.password_pubkey = password_pubkey;
}
/**
* Gets the cyphertext version of the encryption key.
* @return: Encryption key in its cyphertext version.
*/
public String getEncryptionKey() {
return encryption_key;
}
public void setEncryptionKey(String encryption_key) {
this.encryption_key = encryption_key;
}
public String getEncryptedBrainkey() {
return encrypted_brainkey;
}
public void setEncryptedBrainkey(String encrypted_brainkey) {
this.encrypted_brainkey = encrypted_brainkey;
}
public String getBrainkeyPubkey() {
return brainkey_pubkey;
}
public void setBrainkeyPubkey(String brainkey_pubkey) {
this.brainkey_pubkey = brainkey_pubkey;
}
public int getBrainkeySequence() {
return brainkey_sequence;
}
public void setBrainkeySequence(int brainkey_sequence) {
this.brainkey_sequence = brainkey_sequence;
}
public String getBrainkeyBackup_date() {
return brainkey_backup_date;
}
public void setBrainkeyBackupDate(String brainkey_backup_date) {
this.brainkey_backup_date = brainkey_backup_date;
}
public String getCreated() {
return created;
}
public void setCreated(String created) {
this.created = created;
}
public String getLastModified() {
return last_modified;
}
public void setLastModified(String last_modified) {
this.last_modified = last_modified;
}
public String getChainId() {
return chain_id;
}
public void setChainId(String chain_id) {
this.chain_id = chain_id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getBackupDate() {
return backup_date;
}
public void setBackupDate(String backup_date) {
this.backup_date = backup_date;
}
}

View file

@ -1,49 +0,0 @@
package cy.agorise.graphenej.models.backup;
import java.util.List;
/**
* This class is used to represent the JSON-formatted version of the file backup containing one or more
* wallets and keys.
*
* Created by nelson on 2/14/17.
*/
public class WalletBackup {
private Wallet[] wallet;
private PrivateKeyBackup[] private_keys;
private LinkedAccount[] linked_accounts;
public WalletBackup(List<Wallet> wallets, List<PrivateKeyBackup> privateKeys, List<LinkedAccount> linkedAccounts){
this.wallet = wallets.toArray(new Wallet[wallets.size()]);
this.private_keys = privateKeys.toArray(new PrivateKeyBackup[privateKeys.size()]);
this.linked_accounts = linkedAccounts.toArray(new LinkedAccount[linkedAccounts.size()]);
}
public Wallet[] getWallets(){
return wallet;
}
public PrivateKeyBackup[] getPrivateKeys(){
return private_keys;
}
public LinkedAccount[] getLinkedAccounts(){
return linked_accounts;
}
public Wallet getWallet(int index){
return wallet[index];
}
public PrivateKeyBackup getPrivateKeyBackup(int index){
return private_keys[index];
}
public int getWalletCount(){
return wallet.length;
}
public int getKeyCount(){
return private_keys.length;
}
}

View file

@ -1,306 +0,0 @@
package cy.agorise.graphenej.objects;
import com.google.common.primitives.Bytes;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import cy.agorise.graphenej.Address;
import cy.agorise.graphenej.PublicKey;
import cy.agorise.graphenej.Util;
import cy.agorise.graphenej.errors.ChecksumException;
import cy.agorise.graphenej.errors.MalformedAddressException;
import cy.agorise.graphenej.interfaces.ByteSerializable;
import cy.agorise.graphenej.interfaces.JsonSerializable;
import cy.agorise.graphenej.operations.TransferOperation;
import org.bitcoinj.core.ECKey;
import org.spongycastle.math.ec.ECPoint;
import java.lang.reflect.Type;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
/**
* Created by nelson on 11/9/16.
*/
public class Memo implements ByteSerializable, JsonSerializable {
public final static String TAG = "Memo";
public static final String KEY_FROM = "from";
public static final String KEY_TO = "to";
public static final String KEY_NONCE = "nonce";
public static final String KEY_MESSAGE = "message";
private Address from;
private Address to;
private long nonce;
private byte[] message;
private String plaintextMessage;
public String getPlaintextMessage() {
if(plaintextMessage == null)
return "";
else
return plaintextMessage;
}
public void setPlaintextMessage(String plaintextMessage) {
this.plaintextMessage = plaintextMessage;
}
/**
* Empty Constructor
*/
public Memo() {
this.from = null;
this.to = null;
this.message = null;
}
/**
* Constructor used for private memos.
* @param from: Address of sender
* @param to: Address of recipient.
* @param nonce: Nonce used in the encryption.
* @param message: Message in ciphertext.
*/
public Memo(Address from, Address to, long nonce, byte[] message){
this.from = from;
this.to = to;
this.nonce = nonce;
this.message = message;
}
/**
* Constructor intended to be used with public memos
* @param message: Message in plaintext.
*/
public Memo(String message){
this.message = message.getBytes();
}
public Address getSource(){
return this.from;
}
public Address getDestination(){
return this.to;
}
public long getNonce(){
return this.nonce;
}
public byte[] getByteMessage(){
return this.message;
}
public String getStringMessage(){
if(this.message != null)
return new String(this.message);
else
return "";
}
/**
* Method used to decrypt memo data.
* @param privateKey: Private key of the sender.
* @param publicKey: Public key of the recipient.
* @param nonce: The nonce.
* @param message: Plaintext message.
* @return: The encrypted version of the message.
*/
public static byte[] encryptMessage(ECKey privateKey, PublicKey publicKey, long nonce, String message){
byte[] encrypted = null;
try {
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
MessageDigest sha512 = MessageDigest.getInstance("SHA-512");
// Getting nonce bytes
String stringNonce = String.format("%d", nonce);
byte[] nonceBytes = Arrays.copyOfRange(Util.hexlify(stringNonce), 0, stringNonce.length());
// Getting shared secret
byte[] secret = publicKey.getKey().getPubKeyPoint().multiply(privateKey.getPrivKey()).normalize().getXCoord().getEncoded();
// SHA-512 of shared secret
byte[] ss = sha512.digest(secret);
byte[] seed = Bytes.concat(nonceBytes, Util.hexlify(Util.bytesToHex(ss)));
// Calculating checksum
byte[] sha256Msg = sha256.digest(message.getBytes());
byte[] checksum = Arrays.copyOfRange(sha256Msg, 0, 4);
// Concatenating checksum + message bytes
byte[] msgFinal = Bytes.concat(checksum, message.getBytes());
// Applying encryption
encrypted = Util.encryptAES(msgFinal, seed);
} catch (NoSuchAlgorithmException ex) {
System.out.println("NoSuchAlgotithmException. Msg:"+ ex.getMessage());
}
return encrypted;
}
/**
* Method used to encrypt memo data.
* @param privateKey: Private key of the sender.
* @param destinationAddress: Address of the recipient.
* @param nonce: The nonce.
* @param message: Plaintext message.
* @return: The encrypted version of the message.
*/
public static byte[] encryptMessage(ECKey privateKey, Address destinationAddress, long nonce, String message){
return encryptMessage(privateKey, destinationAddress.getPublicKey(), nonce, message);
}
/**
* Method used to decrypt memo data.
* @param privateKey: The private key of the recipient.
* @param publicKey: The public key of the sender.
* @param nonce: The nonce.
* @param message: The encrypted message.
* @return: The plaintext version of the enrcrypted message.
* @throws ChecksumException
*/
public static String decryptMessage(ECKey privateKey, PublicKey publicKey, long nonce, byte[] message) throws ChecksumException {
String plaintext = "";
try {
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
MessageDigest sha512 = MessageDigest.getInstance("SHA-512");
// Getting nonce bytes
String stringNonce = String.format("%d", nonce);
byte[] nonceBytes = Arrays.copyOfRange(Util.hexlify(stringNonce), 0, stringNonce.length());
// Getting shared secret
byte[] secret = publicKey.getKey().getPubKeyPoint().multiply(privateKey.getPrivKey()).normalize().getXCoord().getEncoded();
// SHA-512 of shared secret
byte[] ss = sha512.digest(secret);
byte[] seed = Bytes.concat(nonceBytes, Util.hexlify(Util.bytesToHex(ss)));
// Calculating checksum
byte[] sha256Msg = sha256.digest(message);
// Applying decryption
byte[] temp = Util.decryptAES(message, seed);
byte[] checksum = Arrays.copyOfRange(temp, 0, 4);
byte[] decrypted = Arrays.copyOfRange(temp, 4, temp.length);
plaintext = new String(decrypted);
byte[] checksumConfirmation = Arrays.copyOfRange(sha256.digest(decrypted), 0, 4);
boolean checksumVerification = Arrays.equals(checksum, checksumConfirmation);
if(!checksumVerification){
throw new ChecksumException("Invalid checksum found while performing decryption");
}
} catch (NoSuchAlgorithmException e) {
System.out.println("NoSuchAlgotithmException. Msg:"+ e.getMessage());
}
return plaintext;
}
/**
* Method used to decrypt memo data.
* @param privateKey: The private key of the recipient.
* @param sourceAddress: The public address key of the sender.
* @param nonce: The nonce.
* @param message: The encrypted message.
* @return: The plaintext version of the enrcrypted message.
* @throws ChecksumException
*/
public static String decryptMessage(ECKey privateKey, Address sourceAddress, long nonce, byte[] message) throws ChecksumException {
return decryptMessage(privateKey, sourceAddress.getPublicKey(), nonce, message);
}
/**
* Implement metod, serialized this Object
* @return the byte array of this object serialized
*/
@Override
public byte[] toBytes() {
if ((this.from == null) && (this.to == null) && (this.message == null)) {
return new byte[]{(byte) 0};
} else if(this.from == null && this.to == null & this.message != null){
return Bytes.concat(new byte[]{1},
new byte[]{(byte)0},
new byte[]{(byte)0},
new byte[]{(byte)0},
new byte[]{(byte) this.message.length},
this.message);
} else {
byte[] nonceBytes = Util.revertLong(nonce);
ECPoint senderPoint = ECKey.compressPoint(from.getPublicKey().getKey().getPubKeyPoint());
PublicKey senderPublicKey = new PublicKey(ECKey.fromPublicOnly(senderPoint));
ECPoint recipientPoint = ECKey.compressPoint(to.getPublicKey().getKey().getPubKeyPoint());
PublicKey recipientPublicKey = new PublicKey(ECKey.fromPublicOnly(recipientPoint));
return Bytes.concat(new byte[]{1},
senderPublicKey.toBytes(),
recipientPublicKey.toBytes(),
nonceBytes,
new byte[]{(byte) this.message.length},
this.message);
}
}
@Override
public String toJsonString() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public JsonElement toJsonObject() {
JsonObject memoObject = new JsonObject();
if ((this.from == null) && (this.to == null)) {
// Public memo
// TODO: Add public memo support
// memoObject.addProperty(KEY_FROM, "");
// memoObject.addProperty(KEY_TO, "");
// memoObject.addProperty(KEY_NONCE, "");
// memoObject.addProperty(KEY_MESSAGE, Util.bytesToHex(this.message));
return null;
}else{
memoObject.addProperty(KEY_FROM, this.from.toString());
memoObject.addProperty(KEY_TO, this.to.toString());
memoObject.addProperty(KEY_NONCE, String.format("%d", this.nonce));
memoObject.addProperty(KEY_MESSAGE, Util.bytesToHex(this.message));
}
return memoObject;
}
/**
* Class used to deserialize a memo
*/
public static class MemoDeserializer implements JsonDeserializer<Memo> {
@Override
public Memo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
String fromAddress = jsonObject.get(KEY_FROM).getAsString();
String toAddress = jsonObject.get(KEY_TO).getAsString();
long nonce = jsonObject.get(KEY_NONCE).getAsLong();
String msg = jsonObject.get(KEY_MESSAGE).getAsString();
Memo memo = null;
try{
Address from = new Address(fromAddress);
Address to = new Address(toAddress);
byte[] message = Util.hexToBytes(msg);
memo = new Memo(from, to, nonce, message);
}catch(MalformedAddressException e){
System.out.println("MalformedAddressException. Msg: "+e.getMessage());
}
return memo;
}
}
}

Some files were not shown because too many files have changed in this diff Show more