Merge branch 'master' of github.com:Agorise/graphenej

This commit is contained in:
Severiano Jaramillo 2019-09-26 09:37:25 -05:00
commit 1129a92aa3
159 changed files with 8379 additions and 231 deletions

3
.github/FUNDING.yml vendored Normal file
View file

@ -0,0 +1,3 @@
# These are supported funding model platforms
custom: https://www.blockchain.com/btc/payment_request?address=1AFGT5gVj7xhfjgHTuwEoaV56WTCh7Gjf1#BITCOIN_ONLY

1
.gitignore vendored
View file

@ -103,3 +103,4 @@ release.properties
graphenej/build
local.properties

View file

@ -3,11 +3,27 @@ subprojects {
mavenCentral()
}
}
allprojects {
repositories {
mavenCentral()
jcenter()
maven {
url "https://maven.google.com"
}
}
}
buildscript {
repositories {
mavenCentral()
maven {
url 'https://maven.google.com/'
name 'Google'
}
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.0'
classpath 'com.android.tools.build:gradle:3.4.2'
classpath 'com.novoda:bintray-release:0.9.1'
}
}
}

View file

@ -17,8 +17,8 @@
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
VERSION_NAME=0.4.6
VERSION_CODE=9
VERSION_NAME=0.4.7-alpha3
VERSION_CODE=12
GROUP=com.github.bilthon
POM_DESCRIPTION=A Java library for mobile app Developers; Graphene/Bitshares blockchain.

0
gradlew vendored Normal file → Executable file
View file

View file

@ -1,34 +1,54 @@
group 'cy.agorise'
version '0.4.6'
apply plugin: 'com.novoda.bintray-release'
apply plugin: 'com.android.library'
apply from: 'maven-push.gradle'
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
compile 'com.neovisionaries:nv-websocket-client:1.30'
compile 'org.bitcoinj:bitcoinj-core:0.14.3'
compile group: 'com.google.code.gson', name: 'gson', version: '2.8.0'
compile group: "org.tukaani", name: "xz", version: "1.6"
publish {
userOrg = 'bilthon'
groupId = 'cy.agorise.graphenej'
artifactId = 'graphenej'
publishVersion = '0.6.0'
repoName = 'Graphenej'
desc = 'A Java library for mobile app Developers; Graphene/Bitshares blockchain.'
website = 'https://github.com/Agorise/graphenej'
}
android {
compileSdkVersion 24
buildToolsVersion "25.0.0"
compileSdkVersion 28
defaultConfig {
minSdkVersion 9
targetSdkVersion 24
versionCode 9
versionName "0.4.6"
minSdkVersion 14
targetSdkVersion 28
versionCode 12
versionName "0.6.0"
vectorDrawables.useSupportLibrary = true
}
buildTypes {
debug{}
preRelease{}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
defaultConfig {
multiDexEnabled true
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
}
dependencies {
testImplementation group: 'junit', name: 'junit', version: '4.12'
implementation 'com.neovisionaries:nv-websocket-client:1.30'
implementation 'org.bitcoinj:bitcoinj-core:0.14.3'
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'org.tukaani:xz:1.6'
androidTestImplementation 'com.android.support:support-annotations:28.0.0'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test:rules:1.0.2'
// Rx dependencies
api 'io.reactivex.rxjava2:rxandroid:2.1.0'
api 'io.reactivex.rxjava2:rxjava:2.2.2'
api 'com.jakewharton.rxrelay2:rxrelay:2.1.0'
api 'com.squareup.okhttp3:okhttp:3.12.2'
}

View file

@ -0,0 +1,90 @@
package cy.agorise.graphenej;
import android.support.test.runner.AndroidJUnit4;
import android.util.Log;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
import cy.agorise.graphenej.network.FullNode;
import cy.agorise.graphenej.network.LatencyNodeProvider;
import cy.agorise.graphenej.network.NodeLatencyVerifier;
import cy.agorise.graphenej.network.NodeProvider;
import io.reactivex.Observer;
import io.reactivex.disposables.Disposable;
import io.reactivex.subjects.PublishSubject;
@RunWith(AndroidJUnit4.class)
public class NodeLatencyVerifierTest {
private final String TAG = this.getClass().getName();
String[] nodeURLs = new String[]{
"wss://bitshares.openledger.info/ws",
"wss://us.nodes.bitshares.ws",
"wss://eu.nodes.bitshares.ws",
"wss://citadel.li/node",
"wss://api.bts.mobi/ws"
};
@Test
public void testNodeLatencyTest() throws Exception {
ArrayList<FullNode> nodeList = new ArrayList<>();
nodeList.add(new FullNode(nodeURLs[0]));
nodeList.add(new FullNode(nodeURLs[1]));
nodeList.add(new FullNode(nodeURLs[2]));
final NodeLatencyVerifier nodeLatencyVerifier = new NodeLatencyVerifier(nodeList);
PublishSubject subject = nodeLatencyVerifier.start();
final NodeProvider nodeProvider = new LatencyNodeProvider();
subject.subscribe(new Observer<FullNode>() {
int counter = 0;
@Override
public void onSubscribe(Disposable d) {}
@Override
public void onNext(FullNode fullNode) {
Log.i(TAG,String.format("Avg latency: %.2f, url: %s", fullNode.getLatencyValue(), fullNode.getUrl()));
// Updating node provider
nodeProvider.updateNode(fullNode);
List<FullNode> sortedNodes = nodeProvider.getSortedNodes();
for(FullNode node : sortedNodes){
Log.d(TAG,String.format("> %.2f, url: %s", node.getLatencyValue(), node.getUrl()));
}
// Finish test after certain amount of rounds
if(counter > 3){
synchronized (NodeLatencyVerifierTest.this){
nodeLatencyVerifier.stop();
NodeLatencyVerifierTest.this.notifyAll();
}
}
counter++;
}
@Override
public void onError(Throwable e) {
Log.e(TAG,"onError.Msg: "+e.getMessage());
synchronized (NodeLatencyVerifierTest.this){
NodeLatencyVerifierTest.this.notifyAll();
}
}
@Override
public void onComplete() {
Log.d(TAG,"onComplete");
}
});
try {
synchronized(this) {
wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

View file

@ -1,8 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cy.agorise.graphenej"
android:versionCode="9"
android:versionName="0.4.6" >
<uses-sdk android:minSdkVersion="1" />
<application/>
package="cy.agorise.graphenej">
<uses-permission android:name="android.permission.INTERNET" />
<application>
<service
android:name=".api.android.NetworkService"
android:enabled="true"
android:exported="true"/>
</application>
</manifest>

View file

@ -33,17 +33,51 @@ public class AccountOptions implements GrapheneSerializable {
private Vote[] votes;
private Extensions extensions;
/**
* Constructor used to instantiate only the following attributes:
* <ul>
* <li>voting_account</li>
* <li>votes</li>
* <li>extensions</li>
* </ul>
*/
public AccountOptions(){
voting_account = new UserAccount(UserAccount.PROXY_TO_SELF);
this.votes = new Vote[0];
this.extensions = new Extensions();
}
/**
* Constructor used to instantiate only the following attributes:
* <ul>
* <li>voting_account</li>
* <li>votes</li>
* <li>memo_key</li>
* <li>extensions</li>
* </ul>
*/
public AccountOptions(PublicKey memoKey){
this();
this.memo_key = memoKey;
}
/**
* Constructor that can be used to instantiate a version of the AccountOptions object
* with a null reference in the 'voting_account' attribute. This can be used to prevent
* a circular dependency situation when de-serializing the UserAccount instance.
*
* @param memoKey Memo public key used by this account
* @param includeAccount Whether or not to instantiate an UserAccount
*/
public AccountOptions(PublicKey memoKey, boolean includeAccount){
if(includeAccount){
voting_account = new UserAccount(UserAccount.PROXY_TO_SELF);
}
this.memo_key = memoKey;
this.votes = new Vote[0];
this.extensions = new Extensions();
}
//TODO: Implement constructor that takes a Vote array.
public PublicKey getMemoKey() {
@ -149,13 +183,23 @@ public class AccountOptions implements GrapheneSerializable {
*/
public static class AccountOptionsDeserializer implements JsonDeserializer<AccountOptions> {
boolean mIncludeUserAccount;
public AccountOptionsDeserializer(){
this.mIncludeUserAccount = true;
}
public AccountOptionsDeserializer(boolean includeUserAccount){
this.mIncludeUserAccount = includeUserAccount;
}
@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());
options = new AccountOptions(address.getPublicKey(), mIncludeUserAccount);
} catch (MalformedAddressException e) {
System.out.println("MalformedAddressException. Msg: "+e.getMessage());
options = new AccountOptions();

View file

@ -1,5 +1,6 @@
package cy.agorise.graphenej;
import com.google.common.base.Objects;
import com.google.common.primitives.Bytes;
import cy.agorise.graphenej.errors.MalformedAddressException;
import org.bitcoinj.core.Base58;
@ -61,4 +62,18 @@ public class Address {
ripemd160Digest.doFinal(checksum, 0);
return Arrays.copyOfRange(checksum, 0, 4);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Address address = (Address) o;
return Objects.equal(publicKey, address.publicKey) &&
Objects.equal(prefix, address.prefix);
}
@Override
public int hashCode() {
return Objects.hashCode(publicKey, prefix);
}
}

View file

@ -191,6 +191,11 @@ public class AssetAmount implements ByteSerializable, JsonSerializable {
return jsonAmount;
}
@Override
public String toString() {
return String.format("(asset=%s, amount=%s)", asset.getObjectId(), amount.toString(10));
}
/**
* Custom serializer used to translate this object into the JSON-formatted entry we need for a transaction.
*/

View file

@ -9,5 +9,10 @@ package cy.agorise.graphenej;
public enum AuthorityType {
OWNER,
ACTIVE,
MEMO
MEMO;
@Override
public String toString() {
return String.format("%d", this.ordinal());
}
}

View file

@ -1,12 +1,19 @@
package cy.agorise.graphenej;
import com.google.gson.JsonArray;
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;
import cy.agorise.graphenej.interfaces.ByteSerializable;
import cy.agorise.graphenej.interfaces.JsonSerializable;
import cy.agorise.graphenej.operations.TransferOperation;
/**
* Created by nelson on 11/5/16.
* Base class that represents a generic operation
*/
public abstract class BaseOperation implements ByteSerializable, JsonSerializable {
@ -32,4 +39,54 @@ public abstract class BaseOperation implements ByteSerializable, JsonSerializabl
array.add(this.getId());
return array;
}
/**
* <p>
* De-serializer used to unpack data from a generic operation. The general format used in the
* JSON-RPC blockchain API is the following:
* </p>
*
* <code>[OPERATION_ID, OPERATION_OBJECT]</code><br>
*
* <p>
* Where <code>OPERATION_ID</code> is one of the operations defined in {@link cy.agorise.graphenej.OperationType}
* and <code>OPERATION_OBJECT</code> is the actual operation serialized in the JSON format.
* </p>
* Here's an example of this serialized form for a transfer operation:<br><br>
*<pre>
*[
* 0,
* {
* "fee": {
* "amount": 264174,
* "asset_id": "1.3.0"
* },
* "from": "1.2.138632",
* "to": "1.2.129848",
* "amount": {
* "amount": 100,
* "asset_id": "1.3.0"
* },
* "extensions": []
* }
*]
*</pre><br>
* If this class is used, this serialized data will be translated to a TransferOperation object instance.<br>
*
* TODO: Add support for operations other than the 'transfer'
*/
public static class OperationDeserializer implements JsonDeserializer<BaseOperation> {
@Override
public BaseOperation deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
BaseOperation operation = null;
if(json.isJsonArray()){
JsonArray array = json.getAsJsonArray();
if(array.get(0).getAsLong() == OperationType.TRANSFER_OPERATION.ordinal()){
operation = context.deserialize(array.get(1), TransferOperation.class);
}
}
return operation;
}
}
}

View file

@ -1,5 +1,7 @@
package cy.agorise.graphenej;
import android.annotation.SuppressLint;
import org.bitcoinj.core.DumpedPrivateKey;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.NetworkParameters;
@ -69,7 +71,15 @@ public class BrainKey {
public BrainKey(String words, int sequence) {
this.mBrainKey = words;
this.sequenceNumber = sequence;
String encoded = String.format("%s %d", words, sequence);
derivePrivateKey();
}
/**
* Generates the actual private key from the brainkey + sequence number
*/
private void derivePrivateKey(){
@SuppressLint("DefaultLocale")
String encoded = String.format("%s %d", this.mBrainKey, this.sequenceNumber);
try {
MessageDigest md = MessageDigest.getInstance("SHA-512");
byte[] bytes = md.digest(encoded.getBytes("UTF-8"));
@ -120,19 +130,28 @@ public class BrainKey {
}
/**
* Brain key words getter
* @return: The word sequence that comprises this brain key
* 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
* 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;
}
/**
* Sequence number setter.
* @param sequenceNumber The sequence number used to generate a specific key from this brainkey
*/
public void setSequenceNumber(int sequenceNumber) {
this.sequenceNumber = sequenceNumber;
derivePrivateKey();
}
}

View file

@ -1,12 +1,17 @@
package cy.agorise.graphenej;
import com.google.gson.JsonArray;
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;
import java.util.ArrayList;
import cy.agorise.graphenej.interfaces.ByteSerializable;
import cy.agorise.graphenej.interfaces.JsonSerializable;
import java.util.ArrayList;
/**
* Created by nelson on 11/9/16.
*/
@ -40,4 +45,15 @@ public class Extensions implements JsonSerializable, ByteSerializable {
public int size(){
return extensions.size();
}
/**
* Custom de-serializer used to avoid problems when de-serializing an object that contains
* an extension array.
*/
public static class ExtensionsDeserializer implements JsonDeserializer<Extensions> {
@Override
public Extensions deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return null;
}
}
}

View file

@ -2,6 +2,8 @@ package cy.agorise.graphenej;
import com.google.gson.annotations.Expose;
import java.util.Locale;
/**
* <p>
* Generic class used to represent a graphene object as defined in
@ -34,15 +36,15 @@ public class GrapheneObject {
/**
*
* @return: A String containing the full object apiId in the form {space}.{type}.{instance}
* @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);
return String.format(Locale.US, "%d.%d.%d", space, type, instance);
}
/**
* Returns the type of this object.
* @return: Instance of the ObjectType enum.
* @return Instance of the ObjectType enum.
*/
public ObjectType getObjectType(){
switch(space){
@ -78,6 +80,8 @@ public class GrapheneObject {
return ObjectType.WORKER_OBJECT;
case 15:
return ObjectType.BALANCE_OBJECT;
case 16:
return ObjectType.HTLC_OBJECT;
}
case IMPLEMENTATION_SPACE:
switch(type){

View file

@ -0,0 +1,43 @@
package cy.agorise.graphenej;
import com.google.gson.JsonElement;
import java.io.ByteArrayOutputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import cy.agorise.graphenej.interfaces.ByteSerializable;
import cy.agorise.graphenej.interfaces.JsonSerializable;
/**
* Class used to represent an existing HTLC contract.
*/
public class Htlc extends GrapheneObject implements ByteSerializable, JsonSerializable {
public Htlc(String id) {
super(id);
}
@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() {
return this.getObjectId();
}
@Override
public JsonElement toJsonObject() {
return null;
}
}

View file

@ -0,0 +1,45 @@
package cy.agorise.graphenej;
import com.google.common.primitives.Bytes;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import cy.agorise.graphenej.interfaces.ByteSerializable;
import cy.agorise.graphenej.interfaces.JsonSerializable;
/**
* Class used to represent a HTLC hash.
*/
public class HtlcHash implements ByteSerializable, JsonSerializable {
private HtlcHashType hashType;
private byte[] hash;
public HtlcHash(HtlcHashType hashType, byte[] hash) {
this.hashType = hashType;
this.hash = hash;
}
public HtlcHashType getType(){
return this.hashType;
}
@Override
public byte[] toBytes() {
byte[] hashTypeBytes = new byte[] { Util.revertInteger(hashType.ordinal())[3] };
return Bytes.concat(hashTypeBytes, hash);
}
@Override
public String toJsonString() {
JsonElement element = toJsonObject();
return element.toString();
}
@Override
public JsonElement toJsonObject() {
JsonArray array = new JsonArray();
array.add(hashType.ordinal());
array.add(Util.byteToString(hash));
return array;
}
}

View file

@ -0,0 +1,11 @@
package cy.agorise.graphenej;
/**
* Used to enumerate the possible hash algorithms used in HTLCs.
* @see <a href="https://github.com/bitshares/bitshares-core/blob/623aea265f2711adade982fc3248e6528dc8ac51/libraries/chain/include/graphene/chain/protocol/htlc.hpp">htlc.hpp</a>
*/
public enum HtlcHashType {
RIPEMD160,
SHA1,
SHA256
}

View file

@ -1,15 +1,11 @@
package cy.agorise.graphenej.objects;
package cy.agorise.graphenej;
import com.google.common.primitives.Bytes;
import com.google.gson.Gson;
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 com.google.gson.*;
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 org.bitcoinj.core.ECKey;
import org.spongycastle.math.ec.ECPoint;
@ -19,20 +15,11 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
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;
/**
* Class used to represent a memo data structure
* {@url https://bitshares.org/doxygen/structgraphene_1_1chain_1_1memo__data.html}
*/
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";
@ -190,10 +177,6 @@ public class Memo implements ByteSerializable, JsonSerializable {
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);
@ -205,7 +188,7 @@ public class Memo implements ByteSerializable, JsonSerializable {
throw new ChecksumException("Invalid checksum found while performing decryption");
}
} catch (NoSuchAlgorithmException e) {
System.out.println("NoSuchAlgotithmException. Msg:"+ e.getMessage());
System.out.println("NoSuchAlgorithmException. Msg:"+ e.getMessage());
}
return plaintext;
}
@ -291,13 +274,15 @@ public class Memo implements ByteSerializable, JsonSerializable {
memoObject.addProperty(KEY_FROM, "");
memoObject.addProperty(KEY_TO, "");
memoObject.addProperty(KEY_NONCE, "");
memoObject.addProperty(KEY_MESSAGE, Util.bytesToHex(this.message));
if(this.message != null)
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("%x", this.nonce));
memoObject.addProperty(KEY_MESSAGE, Util.bytesToHex(this.message));
memoObject.addProperty(KEY_NONCE, this.nonce.toString());
if(this.message != null)
memoObject.addProperty(KEY_MESSAGE, Util.bytesToHex(this.message));
}
return memoObject;
}
@ -310,8 +295,9 @@ public class Memo implements ByteSerializable, JsonSerializable {
*/
public JsonElement toJson(boolean decimal){
JsonElement jsonElement = toJsonObject();
if(decimal){
if(decimal && jsonElement != null){
JsonObject jsonObject = (JsonObject) jsonElement;
// The nonce is interpreted in base 16, but it is going to be written in base 10
BigInteger nonce = new BigInteger(jsonObject.get(KEY_NONCE).getAsString(), 16);
jsonObject.addProperty(KEY_NONCE, nonce.toString());
}

View file

@ -20,6 +20,7 @@ public enum ObjectType {
VESTING_BALANCE_OBJECT,
WORKER_OBJECT,
BALANCE_OBJECT,
HTLC_OBJECT,
GLOBAL_PROPERTY_OBJECT,
DYNAMIC_GLOBAL_PROPERTY_OBJECT,
ASSET_DYNAMIC_DATA,
@ -53,6 +54,7 @@ public enum ObjectType {
case VESTING_BALANCE_OBJECT:
case WORKER_OBJECT:
case BALANCE_OBJECT:
case HTLC_OBJECT:
space = 1;
break;
case GLOBAL_PROPERTY_OBJECT:
@ -123,6 +125,8 @@ public enum ObjectType {
case BALANCE_OBJECT:
type = 15;
break;
case HTLC_OBJECT:
type = 16;
case GLOBAL_PROPERTY_OBJECT:
type = 0;
break;

View file

@ -51,5 +51,15 @@ public enum OperationType {
BLIND_TRANSFER_OPERATION,
TRANSFER_FROM_BLIND_OPERATION,
ASSET_SETTLE_CANCEL_OPERATION, // VIRTUAL
ASSET_CLAIM_FEES_OPERATION
ASSET_CLAIM_FEES_OPERATION,
FBA_DISTRIBUTE_OPERATION,
BID_COLLATERAL_OPERATION,
EXECUTE_BID_OPERATION, // VIRTUAL
ASSET_CLAIM_POOL_OPERATION,
ASSET_UPDATE_ISSUER_OPERATION,
HTLC_CREATE_OPERATION,
HTLC_REDEEM_OPERATION,
HTLC_REDEEMED_OPERATION, // VIRTUAL
HTLC_EXTEND_OPERATION,
HTLC_REFUND_OPERATION // VIRTUAL
}

View file

@ -132,4 +132,8 @@ public class OrderBook {
}
return obtainedBase;
}
public List<LimitOrder> getLimitOrders(){
return limitOrders;
}
}

View file

@ -18,4 +18,13 @@ package cy.agorise.graphenej;
public class Price {
public AssetAmount base;
public AssetAmount quote;
@Override
public String toString() {
return String.format("base:[%s, %s], quote:[%s, %s]",
base.getAsset().getObjectId(),
base.getAmount().toString(),
quote.getAsset().getObjectId(),
quote.getAmount().toString());
}
}

View file

@ -53,4 +53,9 @@ public class PublicKey implements ByteSerializable, Serializable {
PublicKey other = (PublicKey) obj;
return this.publicKey.equals(other.getKey());
}
@Override
public String toString() {
return getAddress();
}
}

View file

@ -14,15 +14,19 @@ public class RPC {
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_FULL_ACCOUNTS = "get_full_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_GET_ACCOUNT_HISTORY = "get_account_history";
public static final String CALL_GET_ACCOUNT_HISTORY_BY_OPERATIONS = "get_account_history_by_operations";
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_GET_ASSETS = "get_assets";
public static final String CALL_GET_OBJECTS = "get_objects";
public static final String CALL_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_BLOCK = "get_block";
@ -30,4 +34,5 @@ public class RPC {
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";
public static final String CALL_GET_TRANSACTION = "get_transaction";
}

View file

@ -20,6 +20,7 @@ import java.lang.reflect.Type;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
@ -97,16 +98,25 @@ public class Transaction implements ByteSerializable, JsonSerializable {
public Transaction(BlockData blockData, List<BaseOperation> operationList){
this.blockData = blockData;
this.operations = operationList;
this.extensions = new Extensions();
}
/**
* Updates the block data
* Block data getter
* @param blockData New block data
*/
public void setBlockData(BlockData blockData){
this.blockData = blockData;
}
/**
* Block data setter
* @return BlockData instance
*/
public BlockData getBlockData(){
return this.blockData;
}
/**
* Updates the fees for all operations in this transaction.
* @param fees: New fees to apply
@ -229,7 +239,12 @@ public class Transaction implements ByteSerializable, JsonSerializable {
// Getting the signature before anything else,
// since this might change the transaction expiration data slightly
byte[] signature = getGrapheneSignature();
byte[] signature = null;
try{
signature = getGrapheneSignature();
}catch(Exception e){
System.out.println("Could not generate signature");
}
// Formatting expiration time
Date expirationTime = new Date(blockData.getExpiration() * 1000);
@ -239,10 +254,12 @@ public class Transaction implements ByteSerializable, JsonSerializable {
// 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);
if(signature != null){
// Adding signature
JsonArray signatureArray = new JsonArray();
signatureArray.add(Util.bytesToHex(signature));
obj.add(KEY_SIGNATURES, signatureArray);
}
JsonArray operationsArray = new JsonArray();
for(BaseOperation operation : operations){
@ -259,7 +276,19 @@ public class Transaction implements ByteSerializable, JsonSerializable {
obj.addProperty(KEY_REF_BLOCK_PREFIX, blockData.getRefBlockPrefix());
return obj;
}
/**
* Method that will return a hash of this transaction's data. The hash covers only the transaction
* attributes and not the signature or the chain id.
*
* @return A hash of the serialized transaction.
*/
public byte[] getHash(){
byte[] txBytes = toBytes();
byte[] toHash = Arrays.copyOfRange(txBytes, 32, txBytes.length); //Tx data only, without chain id
Sha256Hash hash = Sha256Hash.wrap(Sha256Hash.hash(toHash));
return Arrays.copyOfRange(hash.getBytes(), 0, 20); // The hash is only the first 20 bytes
}
/**
@ -291,7 +320,8 @@ public class Transaction implements ByteSerializable, JsonSerializable {
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());
long relativeExpiration = expirationDate.getTime() / 1000;
BlockData blockData = new BlockData(refBlockNum, refBlockPrefix, relativeExpiration);
// Parsing operation list
BaseOperation operation = null;
@ -387,6 +417,26 @@ public class Transaction implements ByteSerializable, JsonSerializable {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.ASSET_CLAIM_FEES_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.FBA_DISTRIBUTE_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.BID_COLLATERAL_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.EXECUTE_BID_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.ASSET_CLAIM_POOL_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.ASSET_UPDATE_ISSUER_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.HTLC_CREATE_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.HTLC_REDEEM_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.HTLC_REDEEMED_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.HTLC_EXTEND_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
} else if (operationId == OperationType.HTLC_REFUND_OPERATION.ordinal()) {
//TODO: Add operation deserialization support
}
if (operation != null) operationList.add(operation);
operation = null;
@ -401,4 +451,9 @@ public class Transaction implements ByteSerializable, JsonSerializable {
return new Transaction(blockData, operationList);
}
}
@Override
public String toString() {
return this.toJsonString();
}
}

View file

@ -47,6 +47,7 @@ public class UserAccount extends GrapheneObject implements ByteSerializable, Jso
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";
public static final String LIFETIME_EXPIRATION_DATE = "1969-12-31T23:59:59";
@Expose
private String name;
@ -84,6 +85,7 @@ public class UserAccount extends GrapheneObject implements ByteSerializable, Jso
@Expose
private long referrerRewardsPercentage;
private boolean isLifeTime;
/**
@ -248,6 +250,14 @@ public class UserAccount extends GrapheneObject implements ByteSerializable, Jso
this.statistics = statistics;
}
public boolean isLifeTime() {
return isLifeTime;
}
public void setLifeTime(boolean lifeTime) {
isLifeTime = lifeTime;
}
/**
* Deserializer used to build a UserAccount instance from the full JSON-formatted response obtained
* by the 'get_objects' API call.
@ -274,8 +284,10 @@ public class UserAccount extends GrapheneObject implements ByteSerializable, Jso
// 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());
String expirationDate = jsonAccount.get(KEY_MEMBERSHIP_EXPIRATION_DATE).getAsString();
Date date = dateFormat.parse(expirationDate);
userAccount.setMembershipExpirationDate(date.getTime());
userAccount.setLifeTime(expirationDate.equals(LIFETIME_EXPIRATION_DATE));
} catch (ParseException e) {
System.out.println("ParseException. Msg: "+e.getMessage());
}

View file

@ -5,6 +5,10 @@ import com.google.common.primitives.UnsignedLong;
import org.spongycastle.crypto.DataLengthException;
import org.spongycastle.crypto.InvalidCipherTextException;
import org.spongycastle.crypto.digests.GeneralDigest;
import org.spongycastle.crypto.digests.RIPEMD160Digest;
import org.spongycastle.crypto.digests.SHA1Digest;
import org.spongycastle.crypto.digests.SHA256Digest;
import org.spongycastle.crypto.engines.AESFastEngine;
import org.spongycastle.crypto.modes.CBCBlockCipher;
import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher;
@ -320,7 +324,9 @@ public class Util {
}
byte[] temp = new byte[count];
System.arraycopy(out, out.length - count, temp, 0, temp.length);
int srcPos = out.length - count > 0 ? out.length - count : 0;
int length = count < out.length ? count : out.length;
System.arraycopy(out, srcPos, temp, 0, length);
byte[] temp2 = new byte[count];
Arrays.fill(temp2, (byte) count);
if (Arrays.equals(temp, temp2)) {
@ -383,4 +389,36 @@ public class Util {
public static long toBase(double value, int precision){
return (long) (value * Math.pow(10, precision));
}
/**
* Creates a hash for HTLC operations.
*
* @param preimage The data we want to operate on.
* @param hashType The type of hash.
* @return The hash.
* @throws NoSuchAlgorithmException
*/
public static byte[] htlcHash(byte[] preimage, HtlcHashType hashType) throws NoSuchAlgorithmException {
byte[] out = null;
GeneralDigest digest = null;
switch(hashType){
case RIPEMD160:
digest = new RIPEMD160Digest();
out = new byte[20];
break;
case SHA1:
digest = new SHA1Digest();
out = new byte[20];
break;
case SHA256:
digest = new SHA256Digest();
out = new byte[32];
break;
default:
throw new IllegalArgumentException("Not supported hash function!");
}
digest.update(preimage, 0, preimage.length);
digest.doFinal(out, 0);
return out;
}
}

View file

@ -0,0 +1,12 @@
package cy.agorise.graphenej.api;
/**
* Class used to list all currently supported API accesses
*/
public class ApiAccess {
public static final int API_NONE = 0x00;
public static final int API_DATABASE = 0x01;
public static final int API_HISTORY = 0x02;
public static final int API_NETWORK_BROADCAST = 0x04;
}

View file

@ -0,0 +1,65 @@
package cy.agorise.graphenej.api;
/**
* Class used to send connection status updates.
*
* Connection status updates can be any of the following:
* - {@link ConnectionStatusUpdate#CONNECTED}
* - {@link ConnectionStatusUpdate#AUTHENTICATED}
* - {@link ConnectionStatusUpdate#API_UPDATE}
* - {@link ConnectionStatusUpdate#DISCONNECTED}
*
* This is specified by the field calle