diff --git a/.travis.yml b/.travis.yml index 9c5679b..3b15523 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,9 @@ jdk: - oraclejdk8 install: true +before_script: + - mvn install:install-file -Dfile=$TRAVIS_BUILD_DIR/libs/java-crypto-conditions-2.0.0-SNAPSHOT.jar -DgroupId=org.interledger -DartifactId=java-crypto-conditions -Dversion=2.0.0-SNAPSHOT -Dpackaging=jar + branches: only: - master diff --git a/pom.xml b/pom.xml index d48bf7f..78b399b 100644 --- a/pom.xml +++ b/pom.xml @@ -68,11 +68,6 @@ commons-codec 1.10 - - org.json - json - 20170516 - com.google.code.gson gson diff --git a/src/main/java/com/authenteq/api/TransactionsApi.java b/src/main/java/com/authenteq/api/TransactionsApi.java index 3874258..bfb6a27 100644 --- a/src/main/java/com/authenteq/api/TransactionsApi.java +++ b/src/main/java/com/authenteq/api/TransactionsApi.java @@ -10,7 +10,6 @@ import com.authenteq.util.NetworkUtils; import okhttp3.RequestBody; import okhttp3.Response; -import org.json.JSONException; import java.io.IOException; import java.util.logging.Logger; @@ -75,20 +74,16 @@ public static Transaction getTransactionById(String id) throws IOException { * @param operation * the operation * @return the transactions by asset id - * @throws JSONException - * the JSON exception * @throws IOException * Signals that an I/O exception has occurred. */ public static Transactions getTransactionsByAssetId(String assetId, Operations operation) - throws JSONException, IOException { + throws IOException { LOGGER.info("getTransactionsByAssetId Call :" + assetId + " operation " + operation); Response response = NetworkUtils.sendGetRequest( BigChainDBGlobals.getBaseUrl() + BigchainDbApi.TRANSACTIONS + "?asset_id=" + assetId + "&operation=" + operation); String body = response.body().string(); response.close(); return JsonUtils.fromJson(body, Transactions.class); - } - } diff --git a/src/main/java/com/authenteq/builders/BigchainDbConfigBuilder.java b/src/main/java/com/authenteq/builders/BigchainDbConfigBuilder.java index 92729fd..4752f50 100644 --- a/src/main/java/com/authenteq/builders/BigchainDbConfigBuilder.java +++ b/src/main/java/com/authenteq/builders/BigchainDbConfigBuilder.java @@ -8,7 +8,6 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Logger; -import com.authenteq.api.AccountApi; import com.authenteq.constants.BigchainDbApi; import com.authenteq.model.ApiEndpoints; import com.authenteq.model.BigChainDBGlobals; @@ -25,9 +24,8 @@ * The Class BigchainDbConfigBuilder. */ public class BigchainDbConfigBuilder { - - private static final Logger LOGGER = Logger.getLogger(AccountApi.class.getName()); - + + private static final Logger LOGGER = Logger.getLogger(BigchainDbConfigBuilder.class.getName()); /** * Instantiates a new bigchain db config builder. */ @@ -74,11 +72,6 @@ public interface ITokens { * Setup. */ void setup(); - - /** - * Setup. - */ - } /** diff --git a/src/main/java/com/authenteq/builders/BigchainDbTransactionBuilder.java b/src/main/java/com/authenteq/builders/BigchainDbTransactionBuilder.java index f221dc5..daf8f45 100644 --- a/src/main/java/com/authenteq/builders/BigchainDbTransactionBuilder.java +++ b/src/main/java/com/authenteq/builders/BigchainDbTransactionBuilder.java @@ -1,24 +1,24 @@ package com.authenteq.builders; import java.io.IOException; -import java.lang.reflect.Type; import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.Signature; import java.security.SignatureException; -import java.util.Map; -import java.util.TreeMap; +import java.util.logging.Logger; + +import com.authenteq.json.strategy.TransactionDeserializer; +import com.authenteq.json.strategy.TransactionsDeserializer; +import com.google.gson.*; import org.apache.commons.codec.binary.Base64; import org.bouncycastle.jcajce.provider.digest.SHA3; import org.interledger.cryptoconditions.types.Ed25519Sha256Condition; import org.interledger.cryptoconditions.types.Ed25519Sha256Fulfillment; -import org.json.JSONObject; import com.authenteq.api.TransactionsApi; import com.authenteq.constants.Operations; import com.authenteq.model.Asset; import com.authenteq.model.Condition; -import com.authenteq.model.DataModel; import com.authenteq.model.Details; import com.authenteq.model.FulFill; import com.authenteq.model.Input; @@ -28,8 +28,6 @@ import com.authenteq.util.DriverUtils; import com.authenteq.util.JsonUtils; import com.authenteq.util.KeyPairUtils; -import com.google.gson.JsonObject; -import com.google.gson.reflect.TypeToken; import net.i2p.crypto.eddsa.EdDSAEngine; import net.i2p.crypto.eddsa.EdDSAPrivateKey; import net.i2p.crypto.eddsa.EdDSAPublicKey; @@ -38,7 +36,8 @@ * The Class BigchainDbTransactionBuilder. */ public class BigchainDbTransactionBuilder { - + private static final Logger LOGGER = Logger.getLogger( BigchainDbTransactionBuilder.class.getName() ); + /** * Instantiates a new bigchain db transaction builder. */ @@ -68,7 +67,7 @@ public interface ITransactionAttributes { */ ITransactionAttributes operation(Operations operation); - /** + /* * Adds the asset. * * @param key @@ -77,7 +76,9 @@ public interface ITransactionAttributes { * the value * @return the i asset meta data */ - ITransactionAttributes addAsset(String key, String value); + //ITransactionAttributes addAsset(String key, String value); + + ITransactionAttributes addAssetDataClass( Class assetDataClass, JsonDeserializer jsonDeserializer ); ITransactionAttributes addOutput(String amount, EdDSAPublicKey... publicKey); @@ -91,17 +92,6 @@ public interface ITransactionAttributes { ITransactionAttributes addInput(String fullfillment, FulFill fullFill, EdDSAPublicKey publicKey); - /** - * Adds the meta data. - * - * @param key - * the key - * @param value - * the value - * @return the i asset meta data - */ - ITransactionAttributes addMetaData(String key, String value); - /** * Adds the assets. * @@ -109,43 +99,34 @@ public interface ITransactionAttributes { * the assets * @return the i asset meta data */ - ITransactionAttributes addAssets(Map assets); - - /** - * Adds the assets. - * - * @param obj - * the obj - * @return the i asset meta data - */ - ITransactionAttributes addAssets(DataModel obj); + ITransactionAttributes addAssets( Object assets, Class assetsDataClass ); /** * Adds the meta data. * - * @param metadata - * the metadata + * @param metaData + * the json object * @return the i asset meta data */ - ITransactionAttributes addMetaData(Map metadata); + ITransactionAttributes addMetaData( Object metaData ); /** - * Adds the meta data. + * Add the class and deserializer for metadata * - * @param obj - * the obj - * @return the i asset meta data + * @param metaDataClass the class of the metadata object + * @param jsonDeserializer the deserializer + * @return */ - ITransactionAttributes addMetaData(DataModel obj); + ITransactionAttributes addMetaDataClassDeserializer( Class metaDataClass, JsonDeserializer jsonDeserializer ); /** - * Adds the meta data. + * Add the class and serializer for metadata * - * @param jsonObject - * the json object - * @return the i asset meta data + * @param metaDataClass the class of the metadata object + * @param jsonSerializer the deserializer + * @return */ - ITransactionAttributes addMetaData(JsonObject jsonObject); + ITransactionAttributes addMetaDataClassSerializer( Class metaDataClass, JsonSerializer jsonSerializer ); /** * Builds the. @@ -220,9 +201,11 @@ public interface IBuild { public static class Builder implements ITransactionAttributes, IBuild { /** The metadata. */ - private Map metadata = null; + private Object metadata = null; + /** The assets. */ - private Map assets = new TreeMap(); + private Object assets = null; + private Class assetsDataClass = null; /** The public key. */ private EdDSAPublicKey publicKey; @@ -280,12 +263,7 @@ public ITransactionAttributes addOutput(String amount, EdDSAPublicKey... publicK @Override public ITransactionAttributes addInput(String fullfillment, FulFill fullFill) { - Input input = new Input(); - input.setFullFillment(fullfillment); - input.setFulFills(fullFill); - input.addOwner(KeyPairUtils.encodePublicKeyInBase58(publicKey)); - this.transaction.addInput(input); - return this; + return addInput( fullfillment, fullFill, this.publicKey ); } @Override @@ -310,71 +288,35 @@ public ITransactionAttributes addInput(String fullfillment, FulFill fullFill, Ed return this; } - /* - * (non-Javadoc) - * - * @see - * com.authenteq.builders.BigchainDbTransactionBuilder.IAssetMetaData# - * addAsset(java.lang.String, java.lang.String) - */ - @Override - public ITransactionAttributes addAsset(String key, String value) { - this.assets.put(key, value); - return this; - } - - /* - * (non-Javadoc) - * - * @see - * com.authenteq.builders.BigchainDbTransactionBuilder.IAssetMetaData# - * addMetaData(java.lang.String, java.lang.String) - */ - @Override - public ITransactionAttributes addMetaData(String key, String value) { - if (this.metadata == null) - this.metadata = new TreeMap(); - this.metadata.put(key, value); + public ITransactionAttributes addAssetDataClass( Class assetDataClass, JsonDeserializer jsonDeserializer ) + { return this; } - /* - * (non-Javadoc) - * - * @see - * com.authenteq.builders.BigchainDbTransactionBuilder.IAssetMetaData# - * addAssets(java.util.Map) + /** + * Add + * @param metaDataClass the class of the metadata object + * @param jsonDeserializer the deserializer + * @return self */ @Override - public ITransactionAttributes addAssets(Map assets) { - this.assets.putAll(assets); + public ITransactionAttributes addMetaDataClassDeserializer( Class metaDataClass, JsonDeserializer jsonDeserializer ) + { + TransactionDeserializer.setMetaDataClass( metaDataClass ); + TransactionsDeserializer.setMetaDataClass( metaDataClass ); + JsonUtils.addTypeAdapterDeserializer( metaDataClass, jsonDeserializer ); return this; } - /* - * (non-Javadoc) - * - * @see - * com.authenteq.builders.BigchainDbTransactionBuilder.IAssetMetaData# - * addMetaData(java.util.Map) - */ - @Override - public ITransactionAttributes addMetaData(Map metadata) { - if( this.metadata == null ) - this.metadata = new TreeMap(); - this.metadata.putAll(metadata); + public ITransactionAttributes addMetaDataClassSerializer( Class metaDataClass, JsonSerializer jsonSerializer ) + { + JsonUtils.addTypeAdapterSerializer( metaDataClass, jsonSerializer ); return this; } - /* - * (non-Javadoc) - * - * @see - * com.authenteq.builders.BigchainDbTransactionBuilder.IAssetMetaData# - * addMetaData(com.google.gson.JsonObject) - */ - @Override - public ITransactionAttributes addMetaData(JsonObject jsonObject) { + public ITransactionAttributes addMetaData( Object object ) + { + this.metadata = object; return this; } @@ -415,22 +357,19 @@ public IBuild build(EdDSAPublicKey publicKey) { this.transaction.setOperation("CREATE"); } - this.transaction.setAsset(new Asset(this.assets)); + this.transaction.setAsset(new Asset(this.assets, this.assetsDataClass)); this.transaction.setMetaData(this.metadata); this.transaction.setVersion("1.0"); - // Workaround to pop out the field. - JSONObject transactionJObject = DriverUtils.makeSelfSorting(new JSONObject(this.transaction.toString())); - transactionJObject.remove("id"); // no need before we sign + String temp = this.transaction.toHashInput(); + LOGGER.info( "TO BE HASHED ---->\n" + temp + "\n<" ); + JsonObject transactionJObject = DriverUtils.makeSelfSortingGson( temp ); SHA3.DigestSHA3 md = new SHA3.DigestSHA3(256); md.update(transactionJObject.toString().getBytes()); String id = DriverUtils.getHex(md.digest()); this.transaction.setId(id); - // we need it after. - transactionJObject.accumulate("id", id); - this.transaction = JsonUtils.fromJson(DriverUtils.makeSelfSorting(transactionJObject).toString(), - Transaction.class); + return this; } @@ -450,7 +389,7 @@ private void sign(EdDSAPrivateKey privateKey) throws InvalidKeyException, SignatureException, NoSuchAlgorithmException { // signing the transaction - JSONObject transactionJObject = DriverUtils.makeSelfSorting(new JSONObject(this.transaction.toString())); + JsonObject transactionJObject = DriverUtils.makeSelfSortingGson(this.transaction.toString()); Signature edDsaSigner = new EdDSAEngine(MessageDigest.getInstance("SHA-512")); edDsaSigner.initSign(privateKey); edDsaSigner.update(transactionJObject.toString().getBytes()); @@ -459,7 +398,6 @@ private void sign(EdDSAPrivateKey privateKey) this.transaction.getInputs().get(0) .setFullFillment(Base64.encodeBase64URLSafeString(fulfillment.getEncoded())); this.transaction.setSigned(true); - } /* @@ -532,37 +470,18 @@ public Transaction sendTransaction() throws IOException { return this.transaction; } - /* - * (non-Javadoc) - * - * @see - * com.authenteq.builders.BigchainDbTransactionBuilder.IAssetMetaData# - * addAssets(com.authenteq.model.DataModel) - */ - @Override - public ITransactionAttributes addAssets(DataModel obj) { - Type mapType = new TypeToken>() { - }.getType(); - Map son = JsonUtils.getGson().fromJson(JsonUtils.toJson(obj), mapType); - this.assets.putAll(son); - return this; - } - - /* - * (non-Javadoc) - * - * @see - * com.authenteq.builders.BigchainDbTransactionBuilder.IAssetMetaData# - * addMetaData(com.authenteq.model.DataModel) + /** + * Add an asset along with the assetDataClass + * + * @param obj the asset data + * @param assetsDataClass the type of the asset data class + * + * @return self */ - @Override - public ITransactionAttributes addMetaData(DataModel obj) { - Type mapType = new TypeToken>() { - }.getType(); - Map son = JsonUtils.getGson().fromJson(JsonUtils.toJson(obj), mapType); - if (this.metadata == null) - this.metadata = new TreeMap(); - this.metadata.putAll(son); + public ITransactionAttributes addAssets( Object obj, Class assetsDataClass ) + { + this.assets = obj; + this.assetsDataClass = assetsDataClass; return this; } diff --git a/src/main/java/com/authenteq/json/strategy/AssetSerializer.java b/src/main/java/com/authenteq/json/strategy/AssetSerializer.java new file mode 100644 index 0000000..622250f --- /dev/null +++ b/src/main/java/com/authenteq/json/strategy/AssetSerializer.java @@ -0,0 +1,32 @@ +package com.authenteq.json.strategy; + +import com.authenteq.model.Asset; +import com.authenteq.util.JsonUtils; +import com.google.gson.*; + +import java.lang.reflect.Type; + +public class AssetSerializer implements JsonSerializer +{ + /** + * Serialize an asset object to json object + * Note: given the type of the asset.data it maybe necessary to + * to add a type adapter {@link JsonSerializer} and/or {@link JsonDeserializer} with {@link JsonUtils} and + * {@link com.authenteq.util.JsonUtils#addTypeAdapterSerializer} + * + * TODO test user.data with custom serializer + * + * @param src object to serialize + * @param typeOfSrc type of src + * @param context the json context + * @return the json object + */ + public JsonElement serialize( Asset src, Type typeOfSrc, JsonSerializationContext context ) + { + Gson gson = JsonUtils.getGson(); + JsonObject asset = new JsonObject(); + asset.add( "data", gson.toJsonTree( src.getData(), src.getDataClass() ) ); + + return asset; + } +} diff --git a/src/main/java/com/authenteq/json/strategy/AssetsDeserializer.java b/src/main/java/com/authenteq/json/strategy/AssetsDeserializer.java index be78fbd..9463d21 100644 --- a/src/main/java/com/authenteq/json/strategy/AssetsDeserializer.java +++ b/src/main/java/com/authenteq/json/strategy/AssetsDeserializer.java @@ -9,9 +9,6 @@ import com.google.gson.JsonParseException; import java.lang.reflect.Type; -import java.util.Iterator; - - /** * The Class AssetsDeserializer. @@ -26,9 +23,7 @@ public Assets deserialize(JsonElement json, Type typeOfT, JsonDeserializationCon throws JsonParseException { Assets assets = new Assets(); - Iterator jsonIter = json.getAsJsonArray().iterator(); - while(jsonIter.hasNext()) { - JsonElement jElement = jsonIter.next(); + for( JsonElement jElement: json.getAsJsonArray() ) { assets.addAsset(JsonUtils.fromJson(jElement.getAsJsonObject().toString(), Asset.class)); } return assets; diff --git a/src/main/java/com/authenteq/json/strategy/TransactionDeserializer.java b/src/main/java/com/authenteq/json/strategy/TransactionDeserializer.java index 4d8daa1..3e91e42 100644 --- a/src/main/java/com/authenteq/json/strategy/TransactionDeserializer.java +++ b/src/main/java/com/authenteq/json/strategy/TransactionDeserializer.java @@ -10,13 +10,23 @@ import com.google.gson.JsonElement; import com.google.gson.JsonParseException; import java.lang.reflect.Type; -import java.util.Iterator; import java.util.Map; /** * The Class TransactionsDeserializer. */ public class TransactionDeserializer implements JsonDeserializer { + private static Class metaDataClass = Map.class; + + /** + * Set a custom metadata class the class that is serialized should be symmetrical with the class that is deserialized. + * + * @param metaDataType the metaData class + */ + public static void setMetaDataClass( Class metaDataType ) + { + metaDataClass = metaDataType; + } /* * (non-Javadoc) @@ -27,37 +37,26 @@ public class TransactionDeserializer implements JsonDeserializer { */ @SuppressWarnings("unchecked") @Override - public Transaction deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { - - // while(jsonIter.hasNext()) { + public Transaction deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException + { Transaction transaction = new Transaction(); JsonElement jElement = json.getAsJsonObject(); - transaction.setAsset(JsonUtils.fromJson(jElement.getAsJsonObject().get("asset").toString(), Asset.class)); - transaction.setMetaData((Map) JsonUtils - .fromJson(jElement.getAsJsonObject().get("metadata").toString(), Map.class)); + transaction.setAsset( JsonUtils.fromJson(jElement.getAsJsonObject().get("asset").toString(), Asset.class)); + transaction.setMetaData( JsonUtils.fromJson( jElement.getAsJsonObject().get("metadata").toString(), metaDataClass )); transaction.setId(jElement.getAsJsonObject().get("id").toString().replace("\"", "")); - Iterator jInputElementIter = jElement.getAsJsonObject().get("inputs").getAsJsonArray().iterator(); - - while (jInputElementIter.hasNext()) { - JsonElement jInputElement = jInputElementIter.next(); + for( JsonElement jInputElement: jElement.getAsJsonObject().get("inputs").getAsJsonArray() ) { transaction.addInput(JsonUtils.fromJson(jInputElement.toString(), Input.class)); } - Iterator jOutputElementIter = jElement.getAsJsonObject().get("outputs").getAsJsonArray() - .iterator(); - - while (jOutputElementIter.hasNext()) { - JsonElement jOutputElement = jOutputElementIter.next(); + for( JsonElement jOutputElement: jElement.getAsJsonObject().get("outputs").getAsJsonArray() ) { transaction.addOutput(JsonUtils.fromJson(jOutputElement.toString(), Output.class)); } transaction.setOperation(jElement.getAsJsonObject().get("operation").toString()); transaction.setVersion(jElement.getAsJsonObject().get("version").toString()); - - // } + return transaction; } } diff --git a/src/main/java/com/authenteq/json/strategy/TransactionIdExclusionStrategy.java b/src/main/java/com/authenteq/json/strategy/TransactionIdExclusionStrategy.java new file mode 100644 index 0000000..0916f23 --- /dev/null +++ b/src/main/java/com/authenteq/json/strategy/TransactionIdExclusionStrategy.java @@ -0,0 +1,18 @@ +package com.authenteq.json.strategy; + +import com.authenteq.model.Transaction; +import com.google.gson.ExclusionStrategy; +import com.google.gson.FieldAttributes; + +public class TransactionIdExclusionStrategy implements ExclusionStrategy +{ + public boolean shouldSkipClass( Class klass ) + { + return false; + } + + public boolean shouldSkipField( FieldAttributes f ) + { + return f.getDeclaringClass() == Transaction.class && f.getName().equals( "id" ); + } +} diff --git a/src/main/java/com/authenteq/json/strategy/TransactionsDeserializer.java b/src/main/java/com/authenteq/json/strategy/TransactionsDeserializer.java index 4f7da2e..88d400c 100644 --- a/src/main/java/com/authenteq/json/strategy/TransactionsDeserializer.java +++ b/src/main/java/com/authenteq/json/strategy/TransactionsDeserializer.java @@ -8,15 +8,18 @@ import com.google.gson.JsonParseException; import java.lang.reflect.Type; -import java.util.Iterator; import java.util.Map; - - /** * The Class TransactionsDeserializer. */ public class TransactionsDeserializer implements JsonDeserializer { + private static Class metaDataClass = Map.class; + + public static void setMetaDataClass( Class metaDataType ) + { + metaDataClass = metaDataType; + } /* (non-Javadoc) * @see com.google.gson.JsonDeserializer#deserialize(com.google.gson.JsonElement, java.lang.reflect.Type, com.google.gson.JsonDeserializationContext) @@ -27,26 +30,19 @@ public Transactions deserialize(JsonElement json, Type typeOfT, JsonDeserializat throws JsonParseException { Transactions transactions = new Transactions(); - Iterator jsonIter = json.getAsJsonArray().iterator(); - while(jsonIter.hasNext()) { + + for( JsonElement jElement: json.getAsJsonArray() ) { Transaction transaction = new Transaction(); - JsonElement jElement = jsonIter.next(); transaction.setAsset(JsonUtils.fromJson(jElement.getAsJsonObject().get("asset").toString(), Asset.class)); - transaction.setMetaData((Map)JsonUtils.fromJson(jElement.getAsJsonObject().get("metadata").toString(), Map.class)); + transaction.setMetaData( JsonUtils.fromJson( jElement.getAsJsonObject().get("metadata").toString(), metaDataClass )); transaction.setId(jElement.getAsJsonObject().get("id").toString()); - - Iterator jInputElementIter = jElement.getAsJsonObject().get("inputs").getAsJsonArray().iterator(); - - while(jInputElementIter.hasNext()) { - JsonElement jInputElement = jInputElementIter.next(); + + for( JsonElement jInputElement: jElement.getAsJsonObject().get("inputs").getAsJsonArray() ) { transaction.addInput(JsonUtils.fromJson(jInputElement.toString(), Input.class)); } - - Iterator jOutputElementIter = jElement.getAsJsonObject().get("outputs").getAsJsonArray().iterator(); - - while(jOutputElementIter.hasNext()) { - JsonElement jOutputElement = jOutputElementIter.next(); + + for( JsonElement jOutputElement: jElement.getAsJsonObject().get("outputs").getAsJsonArray() ) { transaction.addOutput(JsonUtils.fromJson(jOutputElement.toString(), Output.class)); } diff --git a/src/main/java/com/authenteq/model/Asset.java b/src/main/java/com/authenteq/model/Asset.java index 8e01021..92b15ac 100644 --- a/src/main/java/com/authenteq/model/Asset.java +++ b/src/main/java/com/authenteq/model/Asset.java @@ -4,12 +4,8 @@ import com.google.gson.annotations.SerializedName; import java.io.Serializable; -import java.util.Map; -import java.util.TreeMap; - - -/** +/* * The Class Asset. */ public class Asset implements Serializable { @@ -26,7 +22,11 @@ public class Asset implements Serializable { /** The data. */ @SerializedName("data") - private Map data; + private Object data; + + /** the data class the type of the data class needed for serialization/deserialization */ + @Exclude + private Class dataClass; /** * Instantiates a new asset. @@ -37,9 +37,11 @@ public Asset() {} * Instantiates a new asset. * * @param data the data + * @param dataClass due to type erasure the data class needs to be provided for serialization/deserialization */ - public Asset(Map data) { + public Asset(Object data, Class dataClass ) { this.data = data; + this.dataClass = dataClass; } /** @@ -47,17 +49,18 @@ public Asset(Map data) { * * @return the data */ - public Map getData() { + public Object getData() { return data; } /** - * Sets the data. + * return the type of the Asset data class * - * @param data the data + * @return the data class type */ - public void setData(Map data) { - this.data = data; + public Class getDataClass() + { + return dataClass; } /** @@ -77,23 +80,4 @@ public String getId() { public void setId(String id) { this.id = id; } - - - /** - * Adds the asset. - * - * @param key the key - * @param value the value - * @return the asset - */ - public Asset addAsset(String key, String value) { - if(this.data == null) { - this.data = new TreeMap(); - } - this.data.put(key, value); - return this; - } - - - } diff --git a/src/main/java/com/authenteq/model/Transaction.java b/src/main/java/com/authenteq/model/Transaction.java index 05a7d5f..332f375 100644 --- a/src/main/java/com/authenteq/model/Transaction.java +++ b/src/main/java/com/authenteq/model/Transaction.java @@ -1,16 +1,13 @@ package com.authenteq.model; import com.authenteq.annotations.Exclude; +import com.authenteq.json.strategy.TransactionIdExclusionStrategy; import com.authenteq.util.JsonUtils; import com.google.gson.annotations.SerializedName; import java.io.Serializable; import java.util.ArrayList; import java.util.List; -import java.util.Map; -import java.util.TreeMap; - - /** * The Class Transaction. @@ -27,11 +24,11 @@ public class Transaction implements Serializable { /** The inputs. */ @SerializedName("inputs") - private List inputs = new ArrayList(); + private List inputs = new ArrayList<>(); /** The meta data. */ @SerializedName("metadata") - private Map metaData = null; + private Object metaData = null; /** The operation. */ @SerializedName("operation") @@ -39,7 +36,7 @@ public class Transaction implements Serializable { /** The outputs. */ @SerializedName("outputs") - private List outputs = new ArrayList(); + private List outputs = new ArrayList<>(); /** The version. */ @SerializedName("version") @@ -126,17 +123,18 @@ public void setInputs(List inputs) { * * @return the meta data */ - public Map getMetaData() { + public Object getMetaData() { return metaData; } /** - * Sets the meta data. + * Set the metaData object * - * @param metaData the meta data + * @param obj the metadata object */ - public void setMetaData(Map metaData) { - this.metaData = metaData; + public void setMetaData( Object obj ) + { + this.metaData = obj; } /** @@ -211,20 +209,6 @@ public void addOutput(Output output) { this.outputs.add(output); } - /** - * Adds the meta data. - * - * @param key the key - * @param value the value - * @return the transaction - */ - public Transaction addMetaData(String key, String value) { - if( this.metaData == null ) - this.metaData = new TreeMap(); - this.metaData.put(key, value); - return this; - } - /* (non-Javadoc) * @see java.lang.Object#toString() */ @@ -232,4 +216,14 @@ public Transaction addMetaData(String key, String value) { public String toString() { return JsonUtils.toJson(this); } + + /** + * Return the transaction suitable for hashing. + * + * @return the transaction as a json string + */ + public String toHashInput() + { + return JsonUtils.toJson( this, new TransactionIdExclusionStrategy() ); + } } diff --git a/src/main/java/com/authenteq/model/TransactionModel.java b/src/main/java/com/authenteq/model/TransactionModel.java index c1bd25b..c800498 100644 --- a/src/main/java/com/authenteq/model/TransactionModel.java +++ b/src/main/java/com/authenteq/model/TransactionModel.java @@ -22,6 +22,10 @@ import com.authenteq.util.Base58; import com.authenteq.util.DriverUtils; import com.authenteq.util.KeyPairUtils; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; +import com.google.gson.JsonObject; import net.i2p.crypto.eddsa.EdDSAEngine; import net.i2p.crypto.eddsa.EdDSAPrivateKey; import net.i2p.crypto.eddsa.EdDSAPublicKey; @@ -32,16 +36,11 @@ import org.bouncycastle.jcajce.provider.digest.SHA3; import org.interledger.cryptoconditions.types.Ed25519Sha256Condition; import org.interledger.cryptoconditions.types.Ed25519Sha256Fulfillment; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; import java.security.*; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; - /** * The Class Transaction. */ @@ -51,13 +50,13 @@ public class TransactionModel { private EdDSAPublicKey publicKey; /** The data. */ - private JSONObject data; + private JsonObject data; /** The metadata. */ - private JSONObject metadata; + private JsonObject metadata; /** The transaction json. */ - private JSONObject transactionJson; + private JsonObject transactionJson; /** The signed. */ private boolean signed; @@ -70,10 +69,10 @@ public class TransactionModel { * (can be `null` if not needed) * @param publicKey the public key */ - public TransactionModel(JSONObject data, JSONObject metadata, EdDSAPublicKey publicKey) { + public TransactionModel(JsonObject data, JsonObject metadata, EdDSAPublicKey publicKey) { this.publicKey = publicKey; - this.data = DriverUtils.makeSelfSorting(data); - this.metadata = DriverUtils.makeSelfSorting(metadata); + this.data = DriverUtils.makeSelfSortingGson(data); + this.metadata = DriverUtils.makeSelfSortingGson(metadata); buildTransactionJson(); } @@ -86,12 +85,12 @@ public TransactionModel(JSONObject data, JSONObject metadata, EdDSAPublicKey pub * @param publicKey the public key * @param signed the signed */ - private TransactionModel(JSONObject data, JSONObject metadata, JSONObject transactionJson, - EdDSAPublicKey publicKey, boolean signed) { + private TransactionModel( JsonObject data, JsonObject metadata, JsonObject transactionJson, + EdDSAPublicKey publicKey, boolean signed) { this.publicKey = publicKey; - this.data = DriverUtils.makeSelfSorting(data); - this.metadata = DriverUtils.makeSelfSorting(metadata); - this.transactionJson = DriverUtils.makeSelfSorting(transactionJson); + this.data = DriverUtils.makeSelfSortingGson(data); + this.metadata = DriverUtils.makeSelfSortingGson(metadata); + this.transactionJson = DriverUtils.makeSelfSortingGson(transactionJson); this.signed = signed; } @@ -99,50 +98,50 @@ private TransactionModel(JSONObject data, JSONObject metadata, JSONObject transa * Builds the transaction JSON without actually signing it. */ protected void buildTransactionJson() { - JSONObject asset = DriverUtils.getSelfSortingJson(); - JSONObject outputs = DriverUtils.getSelfSortingJson(); - JSONObject inputs = DriverUtils.getSelfSortingJson(); - JSONObject condition = DriverUtils.getSelfSortingJson(); - JSONObject details = DriverUtils.getSelfSortingJson(); - JSONArray inputsArr = new JSONArray(); - JSONArray outputsArr = new JSONArray(); + JsonObject asset = DriverUtils.getSelfSortingJson(); + JsonObject outputs = DriverUtils.getSelfSortingJson(); + JsonObject inputs = DriverUtils.getSelfSortingJson(); + JsonObject condition = DriverUtils.getSelfSortingJson(); + JsonObject details = DriverUtils.getSelfSortingJson(); + JsonArray inputsArr = new JsonArray(); + JsonArray outputsArr = new JsonArray(); Ed25519Sha256Condition condition1 = new Ed25519Sha256Condition(publicKey); - JSONObject rootObject = DriverUtils.getSelfSortingJson(); + JsonObject rootObject = DriverUtils.getSelfSortingJson(); try { if (metadata == null) { - rootObject.put("metadata", JSONObject.NULL); + rootObject.add("metadata", null); } else { - rootObject.put("metadata", metadata); + rootObject.add("metadata", metadata); } - rootObject.put("operation", "CREATE"); - rootObject.put("version", "1.0"); - asset.put("data", data); - rootObject.put("asset", asset); - - outputs.put("amount", "1"); - JSONArray publicKeys = new JSONArray(); - publicKeys.put(KeyPairUtils.encodePublicKeyInBase58(publicKey)); - outputs.put("public_keys", publicKeys); - outputsArr.put(outputs); - rootObject.put("outputs", outputsArr); - - condition.put("uri", condition1.getUri().toString()); - - details.put("public_key", KeyPairUtils.encodePublicKeyInBase58(publicKey)); - details.put("type", "ed25519-sha-256"); - condition.put("details", details); - outputs.put("condition", condition); - - inputs.put("fulfillment", JSONObject.NULL); - inputs.put("fulfills", JSONObject.NULL); - JSONArray ownersBefore = new JSONArray(); - ownersBefore.put(KeyPairUtils.encodePublicKeyInBase58(publicKey)); - inputs.put("owners_before", ownersBefore); - inputsArr.put(inputs); - rootObject.put("inputs", inputsArr); + rootObject.addProperty("operation", "CREATE"); + rootObject.addProperty("version", "1.0"); + asset.add("data", data); + rootObject.add("asset", asset); + + outputs.addProperty("amount", "1"); + JsonArray publicKeys = new JsonArray(); + publicKeys.add(KeyPairUtils.encodePublicKeyInBase58(publicKey)); + outputs.add("public_keys", publicKeys); + outputsArr.add(outputs); + rootObject.add("outputs", outputsArr); + + condition.addProperty("uri", condition1.getUri().toString()); + + details.addProperty("public_key", KeyPairUtils.encodePublicKeyInBase58(publicKey)); + details.addProperty("type", "ed25519-sha-256"); + condition.add("details", details); + outputs.add("condition", condition); + + inputs.add("fulfillment", null); + inputs.add("fulfills", null); + JsonArray ownersBefore = new JsonArray(); + ownersBefore.add(KeyPairUtils.encodePublicKeyInBase58(publicKey)); + inputs.add("owners_before", ownersBefore); + inputsArr.add(inputs); + rootObject.add("inputs", inputsArr); // getting SHA3 hash of the current JSON object SHA3.DigestSHA3 md = new SHA3.DigestSHA3(256); @@ -150,8 +149,8 @@ protected void buildTransactionJson() { String id = DriverUtils.getHex(md.digest()); // putting the hash as id field - rootObject.put("id", id); - } catch (JSONException e) { + rootObject.addProperty("id", id); + } catch (Exception e) { e.printStackTrace(); } @@ -166,7 +165,7 @@ protected void buildTransactionJson() { * fulfillment */ public String getTransactionId() { - return transactionJson.getString("id"); + return transactionJson.get("id").getAsString(); } /** @@ -184,10 +183,10 @@ public void signTransaction(EdDSAPrivateKey privateKey) throws InvalidKeyExcepti edDsaSigner.update(transactionJson.toString().getBytes()); byte[] signature = edDsaSigner.sign(); Ed25519Sha256Fulfillment fulfillment = new Ed25519Sha256Fulfillment(publicKey, signature); - JSONObject inputs = transactionJson.getJSONArray("inputs").getJSONObject(0); - inputs.put("fulfillment", Base64.encodeBase64URLSafeString(fulfillment.getEncoded())); + JsonObject inputs = transactionJson.get("inputs").getAsJsonArray().get( 0 ).getAsJsonObject();// getJsonArray("inputs").getJSONObject(0); + inputs.addProperty("fulfillment", Base64.encodeBase64URLSafeString(fulfillment.getEncoded())); signed = true; - } catch (JSONException | NoSuchAlgorithmException e) { + } catch ( NoSuchAlgorithmException e) { e.printStackTrace(); } } @@ -206,7 +205,7 @@ public boolean isSigned() { * * @return The JSON representation of the transaction */ - public JSONObject getTransactionJson() { + public JsonObject getTransactionJson() { return transactionJson; } @@ -236,7 +235,7 @@ public EdDSAPublicKey getPublicKey() { * * @return the data */ - public JSONObject getData() { + public JsonObject getData() { return data; } @@ -245,7 +244,7 @@ public JSONObject getData() { * * @return the metadata */ - public JSONObject getMetadata() { + public JsonObject getMetadata() { return metadata; } @@ -257,21 +256,20 @@ public JSONObject getMetadata() { * @param jsonObject the json object * @return the transaction */ - public static TransactionModel createFromJson(JSONObject jsonObject) { - JSONObject data = jsonObject.getJSONObject("asset").getJSONObject("data"); - JSONObject metadata = jsonObject.getJSONObject("metadata"); - String publicKeyEncoded = jsonObject.getJSONArray("outputs").getJSONObject(0).getJSONArray("public_keys") - .getString(0); + public static TransactionModel createFromJson(JsonObject jsonObject) { + JsonObject data = jsonObject.get("asset").getAsJsonObject().get("data").getAsJsonObject(); + JsonObject metadata = jsonObject.get("metadata").getAsJsonObject(); + String publicKeyEncoded = jsonObject.get("outputs").getAsJsonArray().get(0).getAsJsonObject().get("public_keys").getAsJsonArray().get(0).getAsString(); byte[] publicKey = Base58.decode(publicKeyEncoded); EdDSAParameterSpec keySpecs = EdDSANamedCurveTable.getByName("Ed25519"); EdDSAPublicKeySpec spec = new EdDSAPublicKeySpec(publicKey, keySpecs); EdDSAPublicKey edDSAPublicKey = new EdDSAPublicKey(spec); // TODO: validate the signature - Object fulfObject = jsonObject.getJSONArray("inputs").getJSONObject(0).get("fulfillment"); + JsonElement fulfObject = jsonObject.get("inputs").getAsJsonArray().get(0).getAsJsonObject().get("fulfillment"); boolean signed = false; - if (!JSONObject.NULL.equals(fulfObject)) + if ( ! fulfObject.equals( JsonNull.INSTANCE ) ) signed = true; return new TransactionModel(data, metadata, jsonObject, edDSAPublicKey, signed); @@ -283,25 +281,23 @@ public static TransactionModel createFromJson(JSONObject jsonObject) { * @param jsonArray the json array * @return the list */ - public static List createFromJsonArray(JSONArray jsonArray) { + public static List createFromJsonArray(JsonArray jsonArray) { List bigChaindbTransactionList = new ArrayList(); - Iterator jsonObjectIter = jsonArray.iterator(); - while (jsonObjectIter.hasNext()) { - JSONObject jsonObject = (JSONObject)jsonObjectIter.next(); - JSONObject data = jsonObject.getJSONObject("asset").getJSONObject("data"); - JSONObject metadata = jsonObject.getJSONObject("metadata"); - String publicKeyEncoded = jsonObject.getJSONArray("outputs").getJSONObject(0).getJSONArray("public_keys") - .getString(0); + for( JsonElement jsonElement: jsonArray ) { + JsonObject jsonObject = jsonElement.getAsJsonObject(); + JsonObject data = jsonObject.get("asset").getAsJsonObject().get("data").getAsJsonObject(); + JsonObject metadata = jsonObject.get("metadata").getAsJsonObject(); + String publicKeyEncoded = jsonObject.get("outputs").getAsJsonArray().get(0).getAsJsonObject().get("public_keys").getAsJsonArray().get(0).getAsString(); byte[] publicKey = Base58.decode(publicKeyEncoded); EdDSAParameterSpec keySpecs = EdDSANamedCurveTable.getByName("Ed25519"); EdDSAPublicKeySpec spec = new EdDSAPublicKeySpec(publicKey, keySpecs); EdDSAPublicKey edDSAPublicKey = new EdDSAPublicKey(spec); // TODO: validate the signature - Object fulfObject = jsonObject.getJSONArray("inputs").getJSONObject(0).get("fulfillment"); + JsonElement fulfObject = jsonObject.get("inputs").getAsJsonArray().get(0).getAsJsonObject().get("fulfillment"); boolean signed = false; - if (!JSONObject.NULL.equals(fulfObject)) + if ( ! fulfObject.equals( JsonNull.INSTANCE ) ) signed = true; bigChaindbTransactionList.add(new TransactionModel(data, metadata, jsonObject, edDSAPublicKey, signed)); diff --git a/src/main/java/com/authenteq/util/DriverUtils.java b/src/main/java/com/authenteq/util/DriverUtils.java index 9f81505..18e46a1 100644 --- a/src/main/java/com/authenteq/util/DriverUtils.java +++ b/src/main/java/com/authenteq/util/DriverUtils.java @@ -19,14 +19,10 @@ package com.authenteq.util; -import net.i2p.crypto.eddsa.EdDSAPublicKey; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.lang.reflect.Field; -import java.util.*; - +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; /** * The Class DriverUtils. @@ -54,6 +50,22 @@ public static String getHex(byte[] data) { return new String(outData); } + /** + * To conform with BigchainDB serialization + * + * @param input the json string to sort the properties for + * + * @return the json object + */ + public static JsonObject makeSelfSortingGson( String input ) + { + if( input == null ) + return null; + + JsonParser jsonParser = new JsonParser(); + return makeSelfSortingGson( jsonParser.parse( input ).getAsJsonObject() ); + } + /** * Make self sorting. * @@ -64,54 +76,31 @@ public static String getHex(byte[] data) { We are using a hack to make stardard org.json be automatically sorted by key desc alphabetically */ - public static JSONObject makeSelfSorting(JSONObject input) { + public static JsonObject makeSelfSortingGson(JsonObject input ) { if (input == null) return null; - JSONObject json = new JSONObject(); - Field map = null; - try { - map = json.getClass().getDeclaredField("map"); - } catch (NoSuchFieldException e) { - e.printStackTrace(); - } - if (map == null) { - return json; - } + JsonObject json = new JsonObject(); - map.setAccessible(true);//because the field is private final... - try { - map.set(json, new TreeMap<>()); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - map.setAccessible(false); - - Iterator flavoursIter = input.keys(); - while (flavoursIter.hasNext()) { - String key = flavoursIter.next(); - try { - Object j = input.get(key); - if (j instanceof JSONObject) { - json.put(key, makeSelfSorting((JSONObject) j)); - } else if (j instanceof JSONArray) { - JSONArray h = (JSONArray) j; - List oList = new ArrayList(); - for (int i = 0; i < h.length(); i++) { - Object joi = h.get(i); - if (joi instanceof JSONObject) { - oList.add(makeSelfSorting((JSONObject) joi)); - json.put(key, oList); - } else { - oList.add((String) joi); - json.put(key, oList); - } + for( String key: input.keySet() ) { + JsonElement j = input.get(key); + if (j instanceof JsonObject) { + json.add(key, makeSelfSortingGson((JsonObject) j)); + } else if (j instanceof JsonArray ) { + JsonArray h = (JsonArray) j; + JsonArray oList = new JsonArray(); + for (int i = 0; i < h.size(); i++) { + JsonElement joi = h.get( i ); + if (joi instanceof JsonObject) { + oList.add(makeSelfSortingGson((JsonObject) joi)); + json.add(key, oList); + } else { + oList.add(joi); + json.add(key, oList); } - } else { - json.put(key, j); } - } catch (JSONException e) { - e.printStackTrace(); + } else { + json.add(key, j); } } @@ -126,8 +115,7 @@ public static JSONObject makeSelfSorting(JSONObject input) { /* We need to sort the keys in alphabetical order to sign the transaction successfully. */ - public static JSONObject getSelfSortingJson() { - JSONObject json = makeSelfSorting(new JSONObject()); - return json; + public static JsonObject getSelfSortingJson() { + return makeSelfSortingGson(new JsonObject()); } } diff --git a/src/main/java/com/authenteq/util/JsonUtils.java b/src/main/java/com/authenteq/util/JsonUtils.java index b12fa1f..aea469f 100644 --- a/src/main/java/com/authenteq/util/JsonUtils.java +++ b/src/main/java/com/authenteq/util/JsonUtils.java @@ -2,28 +2,53 @@ import com.authenteq.json.factory.GsonEmptyCheckTypeAdapterFactory; import com.authenteq.json.strategy.*; -import com.authenteq.model.Assets; -import com.authenteq.model.Outputs; -import com.authenteq.model.Transactions; -import com.authenteq.model.Votes; -import com.google.gson.FieldNamingPolicy; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; +import com.authenteq.model.*; +import com.google.gson.*; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * Utility class for handling JSON serialization and deserialization. - * */ public class JsonUtils { /** The gson. */ - private static Gson gson; + private static String jsonDateFormat = "yyyy-MM-dd'T'HH:mm:ssX"; // Assumes Java 7 or higher + + private static Map typeAdaptersDeserialize = new ConcurrentHashMap(16) + {{ + put( Transaction.class.getCanonicalName(), new TypeAdapter( Transaction.class, new TransactionDeserializer() ) ); + put( Transactions.class.getCanonicalName(), new TypeAdapter( Transactions.class, new TransactionsDeserializer() ) ); + put( Assets.class.getCanonicalName(), new TypeAdapter( Assets.class, new AssetsDeserializer() ) ); + put( Outputs.class.getCanonicalName(), new TypeAdapter( Outputs.class, new OutputsDeserializer() ) ); + put( Votes.class.getCanonicalName(), new TypeAdapter( Votes.class, new VoteDeserializer() ) ); + }}; + + private static Map typeAdaptersSerialize = new ConcurrentHashMap(16) + {{ + put( Asset.class.getCanonicalName(), new TypeAdapter( Asset.class, new AssetSerializer() ) ); + }}; /** * Instantiates a new json utils. */ private JsonUtils() { } + + private static synchronized GsonBuilder base() + { + GsonBuilder builder = new GsonBuilder(); + + builder = builder + .serializeNulls() + .disableHtmlEscaping() + .setDateFormat( jsonDateFormat ) + .registerTypeAdapterFactory(new GsonEmptyCheckTypeAdapterFactory()) + .addSerializationExclusionStrategy(new CustomExclusionStrategy()); + + return builder; + } /** * Gets the gson. @@ -31,21 +56,74 @@ private JsonUtils() { * @return the gson */ public static Gson getGson() { - if (gson == null) { - GsonBuilder builder = new GsonBuilder(); - - gson = builder.setPrettyPrinting() - .serializeNulls() - .disableHtmlEscaping() - .setPrettyPrinting() - .registerTypeAdapter(Transactions.class, new TransactionsDeserializer()) - .registerTypeAdapter(Assets.class, new AssetsDeserializer()) - .registerTypeAdapter(Outputs.class, new OutputsDeserializer()) - .registerTypeAdapter(Votes.class, new VoteDeserializer()) - .registerTypeAdapterFactory(new GsonEmptyCheckTypeAdapterFactory()) - .addSerializationExclusionStrategy(new CustomExclusionStrategy()).create(); + GsonBuilder builder = base(); + + for( TypeAdapter value : typeAdaptersDeserialize.values() ) + builder.registerTypeAdapter( value.getType(), value.getSerializer() ); + + for( TypeAdapter value : typeAdaptersSerialize.values() ) + builder.registerTypeAdapter( value.getType(), value.getSerializer() ); + + return builder.create(); + } + + /** + * + * @return the gson + */ + public static Gson getGson( ExclusionStrategy... exclusionStrategies ) { + return getGson( null, exclusionStrategies ); + } + + /** + * + * @return the gson + */ + public static Gson getGson( Class ignoreClass, ExclusionStrategy... exclusionStrategies ) + { + GsonBuilder builder = base(); + + for( TypeAdapter value : typeAdaptersDeserialize.values() ) { + if( ignoreClass != null && value.getType().equals( ignoreClass ) ) + continue; + builder.registerTypeAdapter( value.getType(), value.getSerializer() ); + } + + for( TypeAdapter value : typeAdaptersSerialize.values() ) { + if( ignoreClass != null && value.getType().equals( ignoreClass ) ) + continue; + + builder.registerTypeAdapter( value.getType(), value.getSerializer() ); } - return gson; + + return builder.setExclusionStrategies( exclusionStrategies ).create(); + } + + public static void setJsonDateFormat( final String dateFormat ) + { + jsonDateFormat = dateFormat; + } + + /** + * Add a type adapter + * + * @param type the type (@Class) we're adapting + * @param jsonDeserializer the type's deserializer + */ + public static void addTypeAdapterDeserializer( Class type, JsonDeserializer jsonDeserializer ) + { + typeAdaptersDeserialize.put( type.getCanonicalName(), new TypeAdapter( type, jsonDeserializer ) ); + } + + /** + * Add a type adapter + * + * @param type the type (@Class) we're adapting + * @param jsonSerializer the type's deserializer + */ + public static void addTypeAdapterSerializer( Class type, JsonSerializer jsonSerializer ) + { + typeAdaptersSerialize.put( type.getCanonicalName(), new TypeAdapter( type, jsonSerializer ) ); } /** @@ -77,7 +155,20 @@ public static T fromJson(String json, Class T) { public static String toJson(Object src) { return getGson().toJson(src); } - + + /** + * To json. + * + * @param src + * the object for which Json representation is to be created + * setting for Gson . + * @return Json representation of src. + * @see Gson#toJson(Object) + */ + public static String toJson(Object src, ExclusionStrategy ... exclusionStrategies) { + return getGson( exclusionStrategies ).toJson(src); + } + /* * (non-Javadoc) * @@ -87,5 +178,4 @@ public static String toJson(Object src) { public String toString() { return "JsonUtils []"; } - } diff --git a/src/main/java/com/authenteq/util/TypeAdapter.java b/src/main/java/com/authenteq/util/TypeAdapter.java new file mode 100644 index 0000000..220ac51 --- /dev/null +++ b/src/main/java/com/authenteq/util/TypeAdapter.java @@ -0,0 +1,57 @@ +package com.authenteq.util; + +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonSerializer; + +/** + * container for storing type sdapter information + */ +public class TypeAdapter +{ + private Class type; + private Object serializer; + + /** + * Contruct a type adapter + * + * @param type the class + * @param serializer its serializer + */ + TypeAdapter( Class type, JsonDeserializer serializer ) + { + this.type = type; + this.serializer = serializer; + } + + /** + * Contruct a type adapter + * + * @param type the class + * @param serializer its serializer + */ + TypeAdapter( Class type, JsonSerializer serializer ) + { + this.type = type; + this.serializer = serializer; + } + + /** + * Get the deserializer + * + * @return the deserializer + */ + public Object getSerializer() + { + return this.serializer; + } + + /** + * Get the class for the deserializer + * + * @return the class + */ + public Class getType() + { + return type; + } +} \ No newline at end of file diff --git a/src/test/java/com/authenteq/BigchaindbTransactionTest.java b/src/test/java/com/authenteq/BigchaindbTransactionTest.java index b28e1b5..a4c92eb 100644 --- a/src/test/java/com/authenteq/BigchaindbTransactionTest.java +++ b/src/test/java/com/authenteq/BigchaindbTransactionTest.java @@ -262,6 +262,8 @@ import com.authenteq.model.TransactionModel; import com.authenteq.util.JsonUtils; import com.authenteq.util.KeyPairUtils; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import net.i2p.crypto.eddsa.EdDSAEngine; import net.i2p.crypto.eddsa.EdDSAPrivateKey; import net.i2p.crypto.eddsa.EdDSAPublicKey; @@ -274,11 +276,10 @@ import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.interledger.cryptoconditions.types.Ed25519Sha256Condition; import org.interledger.cryptoconditions.types.Ed25519Sha256Fulfillment; -import org.json.JSONObject; import org.junit.Test; -import sun.security.util.KeyUtil; import java.security.*; +import java.util.TreeMap; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; @@ -289,9 +290,10 @@ * The Class BigchaindbTransactionTest. */ public class BigchaindbTransactionTest { - + private JsonParser jsonParser = new JsonParser(); + /** The Constant SHOULD_BE_FULFILMENT. */ - static final String SHOULD_BE_FULFILMENT = "pGSAIOJUaCNTxPOZO2g7x0h6cFHt4LmgrN1LNGXh9q7IDOKxgUDSvX-fMwu6b-VdHQj9plPncX-XiS-VIgBWHPd13hNlB3G-C6grKqzHYGjEGvcJ_fcfD9wy-QHwN4hEfyvebkAM"; + static final String SHOULD_BE_FULFILMENT = "pGSAIOJUaCNTxPOZO2g7x0h6cFHt4LmgrN1LNGXh9q7IDOKxgUAATCXo2r_eqIeRotaioUYOQzoaZTedmFMzQVfY4VOcCd5VO69SkFO3R6dJ8y-qQ0pEzuHDIOGKILoGOU87iWkI"; /** The Constant JSON_REPR_SIGNED. */ static final String JSON_REPR_SIGNED = "{\n" + @@ -381,16 +383,16 @@ public class BigchaindbTransactionTest { */ @Test public void transactionGenerationTest() throws Exception { - JSONObject data = new JSONObject(); - data.put("expiration", "NLpPB8MpOkQLZJuyn4rXacdQBXOt4OAwQUSAEpipi2Y=\\n"); - data.put("lat", "DP\\/p9q4D7L0IBZr53Dh98N1huD5BGG\\/nZ9zs\\/ydEUzc=\\n"); - data.put("lon", "ZPleIXiR3W4RWzjrdXcqXDYUjPGGQn6JKmMF5OH7T6U=\\n"); - data.put("firstname", "NLpPB8MpOkQLZJuyn4rXacdQBXOt4OAwQUSAEpipi2Y=\\n"); - data.put("lastname", "N4iDURp+thKsn1Mn7csSoU63QJnJxqyz+VNOPUikMMk=\\n"); - data.put("dob", "ZBJhOnJgC\\/E\\/iD2eyh15qWqD3jsyj+k9+2XIDJXvhEE=\\n"); - data.put("sex", "lg52\\/gnwsTWdwUW4teyj4SGQOF7y5C435on9HtW3DwI=\\n"); - data.put("nationality", "MjpcaVsVoR+5E5ov4Z2gAal1cUfYLUypL52nFqx7pyM=\\n"); - data.put("idNumber", "hsDKi81fiXWuNHoZrzezzTMHykjDIrAtiPozzPTkkbM=\\n"); + JsonObject data = new JsonObject(); + data.addProperty("expiration", "NLpPB8MpOkQLZJuyn4rXacdQBXOt4OAwQUSAEpipi2Y=\\n"); + data.addProperty("lat", "DP\\/p9q4D7L0IBZr53Dh98N1huD5BGG\\/nZ9zs\\/ydEUzc=\\n"); + data.addProperty("lon", "ZPleIXiR3W4RWzjrdXcqXDYUjPGGQn6JKmMF5OH7T6U=\\n"); + data.addProperty("firstname", "NLpPB8MpOkQLZJuyn4rXacdQBXOt4OAwQUSAEpipi2Y=\\n"); + data.addProperty("lastname", "N4iDURp+thKsn1Mn7csSoU63QJnJxqyz+VNOPUikMMk=\\n"); + data.addProperty("dob", "ZBJhOnJgC\\/E\\/iD2eyh15qWqD3jsyj+k9+2XIDJXvhEE=\\n"); + data.addProperty("sex", "lg52\\/gnwsTWdwUW4teyj4SGQOF7y5C435on9HtW3DwI=\\n"); + data.addProperty("nationality", "MjpcaVsVoR+5E5ov4Z2gAal1cUfYLUypL52nFqx7pyM=\\n"); + data.addProperty("idNumber", "hsDKi81fiXWuNHoZrzezzTMHykjDIrAtiPozzPTkkbM=\\n"); KeyPair keyPair = retrieveKeyPair(); @@ -405,20 +407,18 @@ public void transactionGenerationTest() throws Exception { (EdDSAPublicKey) keyPair.getPublic()); Signature edDsaSigner = new EdDSAEngine(MessageDigest.getInstance("SHA-512")); - JSONObject rootObject = new JSONObject(bigchaindbTransaction.getTransactionJson().toString()); - String fulfilmentVal = rootObject.getJSONArray("inputs").getJSONObject(0).getString("fulfillment"); - rootObject.getJSONArray("inputs").getJSONObject(0).put("fulfillment", JSONObject.NULL); + JsonObject rootObject = jsonParser.parse( bigchaindbTransaction.getTransactionJson().toString()).getAsJsonObject(); + String fulfilmentVal = rootObject.get("inputs").getAsJsonArray().get(0).getAsJsonObject().get("fulfillment").getAsString(); + rootObject.get("inputs").getAsJsonArray().get(0).getAsJsonObject().add("fulfillment", null); edDsaSigner.initSign(keyPair.getPrivate()); edDsaSigner.update(rootObject.toString().getBytes()); byte[] signature = edDsaSigner.sign(); - Ed25519Sha256Fulfillment fulfillment - = new Ed25519Sha256Fulfillment((EdDSAPublicKey) keyPair.getPublic(), signature); - assertEquals("4eff006ca203061d6bc1100959018f008c0f61a4b53c5d8a333159adf69a7c46", rootObject.getString("id")); - assertEquals("4eff006ca203061d6bc1100959018f008c0f61a4b53c5d8a333159adf69a7c46", bigchaindbTransaction.getTransactionId()); + Ed25519Sha256Fulfillment fulfillment = new Ed25519Sha256Fulfillment((EdDSAPublicKey) keyPair.getPublic(), signature); + assertEquals("7a9f2830564b8d21acb1579a09c34904b694aeeabe670df20752d4d731ec96ea", rootObject.get("id").getAsString()); + assertEquals("7a9f2830564b8d21acb1579a09c34904b694aeeabe670df20752d4d731ec96ea", bigchaindbTransaction.getTransactionId()); assertEquals(SHOULD_BE_FULFILMENT, fulfilmentVal); assertTrue(fulfillment.verify(condition1, rootObject.toString().getBytes())); -// return rootObject.toString(); } /** @@ -428,10 +428,8 @@ public void transactionGenerationTest() throws Exception { */ @Test public void transactionFromJson() throws Exception { - TransactionModel transactionSigned - = TransactionModel.createFromJson(new JSONObject(JSON_REPR_SIGNED)); - TransactionModel transactionUnsigned - = TransactionModel.createFromJson(new JSONObject(JSON_REPR_UNSIGNED)); + TransactionModel transactionSigned = TransactionModel.createFromJson(jsonParser.parse(JSON_REPR_SIGNED).getAsJsonObject()); + TransactionModel transactionUnsigned = TransactionModel.createFromJson(jsonParser.parse(JSON_REPR_UNSIGNED).getAsJsonObject()); String publicEncoded = KeyPairUtils.encodePublicKeyInBase58(transactionSigned.getPublicKey()); assertEquals(SHOULD_BE_PUBLIC_KEY, publicEncoded); @@ -445,12 +443,13 @@ public void transactionFromJson() throws Exception { @Test public void transactionMetaDataIsNull() { KeyPair alice = KeyPairUtils.generateNewKeyPair(); - + + TreeMap assetData = new TreeMap() {{ put( "owner", "alice" ); }}; Transaction transaction = BigchainDbTransactionBuilder.init() - .addAsset( "owner", "alice" ) + .addAssets( assetData, assetData.getClass() ) .buildAndSignOnly( (EdDSAPublicKey) alice.getPublic(), (EdDSAPrivateKey) alice.getPrivate() ); - assertTrue( transaction.toString().contains( "\"metadata\": null" ) ); + assertTrue( transaction.toString().contains( "\"metadata\":null" ) ); Transaction tx = JsonUtils.fromJson( transaction.toString(), Transaction.class ); assertNull( tx.getMetaData() ); } diff --git a/src/test/java/com/authenteq/api/AssetsApiTest.java b/src/test/java/com/authenteq/api/AssetsApiTest.java index aee93d4..202cb99 100644 --- a/src/test/java/com/authenteq/api/AssetsApiTest.java +++ b/src/test/java/com/authenteq/api/AssetsApiTest.java @@ -4,19 +4,19 @@ import java.io.IOException; import java.security.KeyPair; +import java.util.Map; +import java.util.TreeMap; -import com.authenteq.AbstractTest; import com.authenteq.builders.BigchainDbTransactionBuilder; +import com.authenteq.json.strategy.AssetSerializer; +import com.authenteq.model.Asset; import com.authenteq.model.Assets; import com.authenteq.model.StatusCode; import com.authenteq.model.Transaction; import net.i2p.crypto.eddsa.EdDSAPrivateKey; import net.i2p.crypto.eddsa.EdDSAPublicKey; -import org.junit.BeforeClass; import org.junit.Test; -import com.authenteq.builders.BigchainDbConfigBuilder; - import static org.junit.Assert.assertEquals; /** @@ -24,7 +24,6 @@ */ public class AssetsApiTest extends AbstractApiTest { - /** * Test asset search. */ @@ -37,10 +36,11 @@ public void testAssetSearch() // create transaction with unique asset net.i2p.crypto.eddsa.KeyPairGenerator edDsaKpg = new net.i2p.crypto.eddsa.KeyPairGenerator(); KeyPair alice = edDsaKpg.generateKeyPair(); + TreeMap assetData = new TreeMap() {{ put( "uuid", uuid ); }}; Transaction transaction = BigchainDbTransactionBuilder .init() - .addAsset( "uuid", uuid ) + .addAssets( assetData, Map.class ) .buildAndSign( (EdDSAPublicKey) alice.getPublic(), (EdDSAPrivateKey) alice.getPrivate() ) .sendTransaction(); assertEquals( StatusCode.VALID, getStatus( transaction ).getStatus() ); // wait for the transaction to be marked VALID diff --git a/src/test/java/com/authenteq/api/BlocksApiTest.java b/src/test/java/com/authenteq/api/BlocksApiTest.java index 6f05185..cd342da 100644 --- a/src/test/java/com/authenteq/api/BlocksApiTest.java +++ b/src/test/java/com/authenteq/api/BlocksApiTest.java @@ -7,6 +7,8 @@ import java.io.IOException; import java.security.KeyPair; import java.security.spec.InvalidKeySpecException; +import java.util.Map; +import java.util.TreeMap; import com.authenteq.AbstractTest; import org.junit.BeforeClass; @@ -38,13 +40,21 @@ public class BlocksApiTest extends AbstractApiTest @Test public void testBlockSearch() throws InterruptedException { try { - - Transaction transaction = BigchainDbTransactionBuilder.init().addAsset("middlename", "mname") - .addAsset("firstname", "John").addAsset("giddlename", "mname").addAsset("ziddlename", "mname") - .addAsset("lastname", "Smith").addMetaData("what", "My first BigchainDB transaction") - .operation(Operations.CREATE).buildAndSign((EdDSAPublicKey) Account.publicKeyFromHex(publicKey), - (EdDSAPrivateKey) Account.privateKeyFromHex(privateKey)) - .sendTransaction(); + Map metaData = new TreeMap() {{ put( "what", "My first BigchainDB transaction" ); }}; + Map assetData = new TreeMap() {{ + put( "middlename", "mname" ); + put("firstname", "John"); + put( "giddlename", "mname" ); + put( "ziddlename", "mname" ); + put( "lastname", "Smith" ); + }}; + + Transaction transaction = BigchainDbTransactionBuilder + .init() + .addAssets(assetData, TreeMap.class) + .operation(Operations.CREATE) + .buildAndSign((EdDSAPublicKey) Account.publicKeyFromHex(publicKey), (EdDSAPrivateKey) Account.privateKeyFromHex(privateKey)) + .sendTransaction(); assertFalse(BlocksApi.getBlocks(transaction.getId(), BlockStatus.VALID).isEmpty()); diff --git a/src/test/java/com/authenteq/api/OutputsApiTest.java b/src/test/java/com/authenteq/api/OutputsApiTest.java index 6373689..56b0cc1 100644 --- a/src/test/java/com/authenteq/api/OutputsApiTest.java +++ b/src/test/java/com/authenteq/api/OutputsApiTest.java @@ -29,7 +29,6 @@ public class OutputsApiTest extends AbstractApiTest /** * Test. - * @throws InvalidKeySpecException */ @Test public void testOutput() throws InvalidKeySpecException { diff --git a/src/test/java/com/authenteq/api/TransactionApiTest.java b/src/test/java/com/authenteq/api/TransactionApiTest.java index cefe2b0..be93d1c 100644 --- a/src/test/java/com/authenteq/api/TransactionApiTest.java +++ b/src/test/java/com/authenteq/api/TransactionApiTest.java @@ -1,8 +1,9 @@ package com.authenteq.api; -import com.authenteq.AbstractTest; import com.authenteq.builders.BigchainDbTransactionBuilder; import com.authenteq.constants.Operations; +import com.authenteq.json.strategy.TransactionDeserializer; +import com.authenteq.json.strategy.TransactionsDeserializer; import com.authenteq.model.Account; import com.authenteq.model.DataModel; import com.authenteq.model.GenericCallback; @@ -11,11 +12,17 @@ import net.i2p.crypto.eddsa.EdDSAPrivateKey; import net.i2p.crypto.eddsa.EdDSAPublicKey; import okhttp3.Response; +import org.junit.Assert; import org.junit.Test; import java.io.IOException; +import java.math.BigDecimal; import java.security.KeyPair; import java.security.spec.InvalidKeySpecException; +import java.util.ArrayList; +import java.util.Date; +import java.util.Map; +import java.util.TreeMap; import static org.junit.Assert.assertNotNull; @@ -51,15 +58,25 @@ public void testPostTransactionUsingBuilder() throws InvalidKeySpecException { dummyAsset.setDescription("asset"); try { - - Transaction transaction = BigchainDbTransactionBuilder.init().addAsset("middlename", "mname") - .addAsset("firstname", "John").addAsset("giddlename", "mname").addAsset("ziddlename", "mname") - .addAsset("lastname", "Smith").addMetaData("what", "My first BigchainDB transaction") - .addAsset("aa", JsonUtils.toJson(dummyAsset)) - .addMetaData("asa",JsonUtils.toJson(dummyAsset)) - .operation(Operations.CREATE) - .buildAndSign((EdDSAPublicKey)Account.publicKeyFromHex(publicKey), (EdDSAPrivateKey)Account.privateKeyFromHex(privateKey)) - .sendTransaction(); + Map metaData = new TreeMap() {{ + put( "what", "My first BigchainDB transaction" ); + put( "aaa", JsonUtils.toJson( dummyAsset )); + }}; + Map assetData = new TreeMap() {{ + put("middlename", "mname"); + put("firstname", "John"); + put("giddlename", "mname"); + put("ziddlename", "mname"); + put("lastname", "Smith"); + put("aa", JsonUtils.toJson(dummyAsset)); + }}; + Transaction transaction = BigchainDbTransactionBuilder + .init() + .addAssets(assetData, TreeMap.class) + .addMetaData(metaData) + .operation(Operations.CREATE) + .buildAndSign((EdDSAPublicKey)Account.publicKeyFromHex(publicKey), (EdDSAPrivateKey)Account.privateKeyFromHex(privateKey)) + .sendTransaction(); assertNotNull(transaction.toString()); } catch (IOException e) { @@ -81,7 +98,7 @@ public void testPostTransactionOfObjectUsingBuilder() { dummyMeta.setId("id"); dummyMeta.setDescription("meta"); - Transaction transaction = BigchainDbTransactionBuilder.init().addAssets(dummyAsset).addMetaData(dummyMeta) + Transaction transaction = BigchainDbTransactionBuilder.init().addAssets(dummyAsset, ObjectDummy.class).addMetaData(dummyMeta) .operation(Operations.CREATE) .buildAndSign((EdDSAPublicKey) keyPair.getPublic(), (EdDSAPrivateKey) keyPair.getPrivate()) .sendTransaction(); @@ -99,7 +116,13 @@ public void testPostTransactionUsingBuilderWithCallBack() { net.i2p.crypto.eddsa.KeyPairGenerator edDsaKpg = new net.i2p.crypto.eddsa.KeyPairGenerator(); KeyPair keyPair = edDsaKpg.generateKeyPair(); try { - BigchainDbTransactionBuilder.init().addAsset("firstname", "alvin").addMetaData("what", "bigchaintrans") + Map metaData = new TreeMap() {{ put( "what", "bigchaintrans" ); }}; + Map assetData = new TreeMap() {{ + put("firstname", "alvin"); + }}; + BigchainDbTransactionBuilder.init() + .addAssets(assetData, TreeMap.class) + .addMetaData(metaData) .operation(Operations.CREATE) .buildAndSign((EdDSAPublicKey) keyPair.getPublic(), (EdDSAPrivateKey) keyPair.getPrivate()) .sendTransaction(new GenericCallback() { @@ -127,6 +150,40 @@ public void otherError(Response response) { } } + @Test + public void testPostTransactionOfObjectMetaDataUsingBuilder() { + net.i2p.crypto.eddsa.KeyPairGenerator edDsaKpg = new net.i2p.crypto.eddsa.KeyPairGenerator(); + KeyPair keyPair = edDsaKpg.generateKeyPair(); + try { + ObjectDummy dummyAsset = new ObjectDummy(); + dummyAsset.setId("id"); + dummyAsset.setDescription("asset"); + System.out.println(dummyAsset.toMapString()); + + SomeMetaData metaData = new SomeMetaData(); + + Transaction transaction = BigchainDbTransactionBuilder + .init() + .addAssets(dummyAsset, ObjectDummy.class) + .addMetaData(metaData) + .operation(Operations.CREATE) + .buildAndSign((EdDSAPublicKey) keyPair.getPublic(), (EdDSAPrivateKey) keyPair.getPrivate()) + .sendTransaction(); + assertNotNull(transaction.getId()); + + String jsonString = JsonUtils.toJson( transaction ); + TransactionsDeserializer.setMetaDataClass( SomeMetaData.class ); + TransactionDeserializer.setMetaDataClass( SomeMetaData.class ); + Transaction x = JsonUtils.fromJson( jsonString,Transaction.class ); + SomeMetaData resultMetaData = ( SomeMetaData) x.getMetaData(); + Assert.assertEquals( 2, resultMetaData.porperty2.intValue() ); + Assert.assertEquals( 3, resultMetaData.properties.size() ); + Assert.assertEquals( "three", resultMetaData.properties.get( 2 ) ); + } catch (IOException e) { + e.printStackTrace(); + } + } + /** * Test transaction by asset id create. */ @@ -145,7 +202,7 @@ public void testTransactionByAssetIdCreate() { dummyMeta.setId("id"); dummyMeta.setDescription("meta"); - Transaction transaction = BigchainDbTransactionBuilder.init().addAssets(dummyAsset).addMetaData(dummyMeta) + Transaction transaction = BigchainDbTransactionBuilder.init().addAssets(dummyAsset, ObjectDummy.class).addMetaData(dummyMeta) .operation(Operations.CREATE) .buildAndSign((EdDSAPublicKey) keyPair.getPublic(), (EdDSAPrivateKey) keyPair.getPrivate()) .sendTransaction(); @@ -183,6 +240,15 @@ public String getDescription() { public void setDescription(String description) { this.description = description; } + } + public class SomeMetaData + { + public String property1 = "property1"; + public Integer porperty2 = 2; + public BigDecimal property3 = new BigDecimal( "3.3" ); + public int property4 = 4; + public ArrayList properties = new ArrayList() {{ add( "one" ); add( "two" ); add( "three"); }}; + public Date date = new Date(); } }