Skip to content

Commit

Permalink
Merge pull request #50 from DECENTfoundation/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
Marian Vanderka authored May 6, 2019
2 parents 7ea2e95 + c316212 commit 100a7ad
Show file tree
Hide file tree
Showing 38 changed files with 590 additions and 1,762 deletions.
25 changes: 19 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
DCore SDK for JVM
================

Set of APIs for accessing the DCore Blockchain.
Set of APIs for accessing the DCore Blockchain. <br>
If you are looking for other platforms you can find info [below](#official-dcore-sdks-for-other-platforms).

Download
Requirements
--------

Available through the JitPack.io
- [Java](https://www.java.com)
- [Gradle](https://gradle.org)

[![](https://jitpack.io/v/DECENTfoundation/DCoreKt-SDK.svg?style=flat-square)][jitpack]

Installation
--------

Setup
-----
Available through the JitPack.io

[![](https://jitpack.io/v/DECENTfoundation/DCoreKt-SDK.svg?style=flat-square)][jitpack]

Add the JitPack repository to your build file
Add it in your root build.gradle at the end of repositories:
Expand All @@ -37,6 +41,8 @@ If not using `gradle`, check [jitpack] site for instructions.
Usage
-----

You can find example project with SDK usage [here](https://github.com/DECENTfoundation/DCore-SDK-Examples/tree/master/sdk-java-android).

Use `DCoreSdk` to initialize the API.
The `DCoreApi` provides different groups of APIs for accessing the blockchain and default configuration values.

Expand Down Expand Up @@ -75,6 +81,13 @@ val disposable = api.accountApi.transfer(credentials, "1.2.27", amount).subscrib
}
```

Official DCore SDKs for other platforms
----------

- [iOS/Swift](https://github.com/DECENTfoundation/DCoreSwift-SDK)
- [JavaScript/TypeScript/Node.js](https://github.com/DECENTfoundation/DCoreJS-SDK)
- [PHP](https://github.com/DECENTfoundation/DCorePHP-SDK)

References
----------

Expand Down
14 changes: 7 additions & 7 deletions library/src/main/java/ch/decent/sdk/api/BalanceApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package ch.decent.sdk.api

import ch.decent.sdk.DCoreApi
import ch.decent.sdk.DCoreConstants
import ch.decent.sdk.model.Asset
import ch.decent.sdk.model.AssetAmount
import ch.decent.sdk.model.AmountWithAsset
import ch.decent.sdk.model.ChainObject
import ch.decent.sdk.model.VestingBalance
import ch.decent.sdk.net.model.request.GetAccountBalances
Expand Down Expand Up @@ -64,7 +64,7 @@ class BalanceApi internal constructor(api: DCoreApi) : BaseApi(api) {
*
* @return a pair of asset to amount
*/
fun getWithAsset(accountId: ChainObject, assetSymbol: String = DCoreConstants.DCT_SYMBOL): Single<Pair<Asset, AssetAmount>> =
fun getWithAsset(accountId: ChainObject, assetSymbol: String = DCoreConstants.DCT_SYMBOL): Single<AmountWithAsset> =
getAllWithAsset(accountId, listOf(assetSymbol)).map { it.single() }

/**
Expand All @@ -75,10 +75,10 @@ class BalanceApi internal constructor(api: DCoreApi) : BaseApi(api) {
*
* @return a list of pairs of assets to amounts
*/
fun getAllWithAsset(accountId: ChainObject, assetSymbols: List<String>): Single<List<Pair<Asset, AssetAmount>>> =
fun getAllWithAsset(accountId: ChainObject, assetSymbols: List<String>): Single<List<AmountWithAsset>> =
api.assetApi.getAllByName(assetSymbols).flatMap { assets ->
getAll(accountId, assets.map { it.id }).map {
it.map { balance -> assets.single { it.id == balance.assetId } to balance }
it.map { balance -> AmountWithAsset(assets.single { it.id == balance.assetId }, balance) }
}
}

Expand All @@ -90,7 +90,7 @@ class BalanceApi internal constructor(api: DCoreApi) : BaseApi(api) {
*
* @return a pair of asset to amount
*/
fun getWithAsset(name: String, assetSymbol: String = DCoreConstants.DCT_SYMBOL): Single<Pair<Asset, AssetAmount>> =
fun getWithAsset(name: String, assetSymbol: String = DCoreConstants.DCT_SYMBOL): Single<AmountWithAsset> =
getAllWithAsset(name, listOf(assetSymbol)).map { it.single() }

/**
Expand All @@ -101,10 +101,10 @@ class BalanceApi internal constructor(api: DCoreApi) : BaseApi(api) {
*
* @return list of assets with amounts
*/
fun getAllWithAsset(name: String, assetSymbols: List<String>): Single<List<Pair<Asset, AssetAmount>>> =
fun getAllWithAsset(name: String, assetSymbols: List<String>): Single<List<AmountWithAsset>> =
api.assetApi.getAllByName(assetSymbols).flatMap { assets ->
getAll(name, assets.map { it.id }).map {
it.map { balance -> assets.single { it.id == balance.assetId } to balance }
it.map { balance -> AmountWithAsset(assets.single { it.id == balance.assetId }, balance) }
}
}

Expand Down
14 changes: 12 additions & 2 deletions library/src/main/java/ch/decent/sdk/api/ContentApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package ch.decent.sdk.api

import ch.decent.sdk.DCoreApi
import ch.decent.sdk.crypto.Credentials
import ch.decent.sdk.exception.ObjectNotFoundException
import ch.decent.sdk.model.*
import ch.decent.sdk.net.model.request.*
import io.reactivex.Single
Expand All @@ -24,7 +25,17 @@ class ContentApi internal constructor(api: DCoreApi) : BaseApi(api) {
*
* @return a content if found, [ch.decent.sdk.exception.ObjectNotFoundException] otherwise
*/
fun get(contentId: ChainObject): Single<Content> = GetContentById(contentId).toRequest().map { it.single() }
fun get(contentId: ChainObject): Single<Content> = GetContentById(listOf(contentId)).toRequest().map { it.single() }

/**
* Get contents by ids.
*
* @param contentId object ids of the contents, 2.13.*
*
* @return a content if found, empty list otherwise
*/
fun getAll(contentId: List<ChainObject>): Single<List<Content>> = GetContentById(contentId).toRequest()
.onErrorResumeNext { if (it is ObjectNotFoundException) Single.just(emptyList()) else Single.error(it) }

/**
* Get content by uri.
Expand All @@ -35,7 +46,6 @@ class ContentApi internal constructor(api: DCoreApi) : BaseApi(api) {
*/
fun get(uri: String): Single<Content> = GetContentByUri(uri).toRequest()

// todo untested no data
/**
* Get a list of accounts holding publishing manager status.
*
Expand Down
47 changes: 42 additions & 5 deletions library/src/main/java/ch/decent/sdk/api/PurchaseApi.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package ch.decent.sdk.api

import ch.decent.sdk.DCoreApi
import ch.decent.sdk.model.ChainObject
import ch.decent.sdk.model.ObjectType
import ch.decent.sdk.model.Purchase
import ch.decent.sdk.model.SearchPurchasesOrder
import ch.decent.sdk.crypto.Credentials
import ch.decent.sdk.model.*
import ch.decent.sdk.net.model.request.*
import io.reactivex.Single

Expand Down Expand Up @@ -87,11 +85,50 @@ class PurchaseApi internal constructor(api: DCoreApi) : BaseApi(api) {
*
* @return a list of purchase objects
*/
// todo wait for add feedback OP so we can test
fun findAllForFeedback(
uri: String,
user: String? = null,
count: Int = 100,
startId: ChainObject = ObjectType.NULL_OBJECT.genericId
): Single<List<Purchase>> = SearchFeedback(user, uri, startId, count).toRequest()

/**
* Create a rate and comment content operation.
*
* @param uri a uri of the content
* @param consumer object id of the account, 1.2.*
* @param rating 1-5 stars
* @param comment max 100 chars
* @param fee [AssetAmount] fee for the operation, if left [BaseOperation.FEE_UNSET] the fee will be computed in DCT asset
*
* @return a rate and comment content operation
*/
fun createRateAndCommentOperation(
uri: String,
consumer: ChainObject,
rating: Int,
comment: String,
fee: AssetAmount = BaseOperation.FEE_UNSET
): Single<LeaveRatingAndCommentOperation> = Single.just(LeaveRatingAndCommentOperation(uri, consumer, rating, comment, fee))

/**
* Rate and comment content operation.
*
* @param credentials account credentials
* @param uri a uri of the content
* @param rating 1-5 stars
* @param comment max 100 chars
* @param fee [AssetAmount] fee for the operation, if left [BaseOperation.FEE_UNSET] the fee will be computed in DCT asset
*
* @return a rate and comment content operation
*/
fun rateAndComment(
credentials: Credentials,
uri: String,
rating: Int,
comment: String,
fee: AssetAmount = BaseOperation.FEE_UNSET
): Single<TransactionConfirmation> = createRateAndCommentOperation(uri, credentials.account, rating, comment, fee)
.flatMap { api.broadcastApi.broadcastWithCallback(credentials.keyPair, it) }

}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class DumpedPrivateKey private constructor(
fun fromBase58(encoded: String): DumpedPrivateKey {
val versionAndData = Base58.decodeChecked(encoded)
val version = versionAndData[0].toInt() and 0xFF
require(version == 0x80, { "$version is not a valid private key version byte" })
require(version == 0x80) { "$version is not a valid private key version byte" }
return versionAndData.copyOfRange(1, versionAndData.size).let {
if (it.size == 33 && it[32].toInt() == 1) {
DumpedPrivateKey(version, it.copyOfRange(0, it.size - 1), true)
Expand Down
22 changes: 19 additions & 3 deletions library/src/main/java/ch/decent/sdk/crypto/ECKeyPair.kt
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,7 @@ class ECKeyPair {
System.arraycopy(signature.s.bytes(32), 0, sigData, 33, 32)

// canonical tests
return if (sigData[0].toInt() and 0x80 != 0 || sigData[0].toInt() == 0 ||
sigData[1].toInt() and 0x80 != 0 || sigData[32].toInt() and 0x80 != 0 ||
sigData[32].toInt() == 0 || sigData[33].toInt() and 0x80 != 0) {
return if (!checkCanonicalSignature(sigData)) {
""
} else {
Hex.encode(sigData)
Expand Down Expand Up @@ -222,6 +220,23 @@ class ECKeyPair {
compEnc[0] = (if (yBit) 0x03 else 0x02).toByte()
return curve.curve.decodePoint(compEnc)
}

/*
https://github.com/steemit/steem/issues/1944
bool public_key::is_canonical( const compact_signature& c )
{
return !(c.data[1] & 0x80)
&& !(c.data[1] == 0 && !(c.data[2] & 0x80))
&& !(c.data[33] & 0x80)
&& !(c.data[33] == 0 && !(c.data[34] & 0x80));
}
*/
@JvmStatic
fun checkCanonicalSignature(sigData: ByteArray): Boolean =
sigData.map { it.toInt() and 0xFF }.let {
it[1] < 0x80 && !(it[1] == 0 && it[2] < 0x80)
&& it[33] < 0x80 && !(it[33] == 0 && it[34] < 0x80)
}
}

data class ECDSASignature(val r: BigInteger, val s: BigInteger) {
Expand Down Expand Up @@ -259,6 +274,7 @@ fun DumpedPrivateKey.ecKey() = ECKeyPair.fromPrivate(bytes, compressed)
fun ECKeyPair.base58() = this.dpk().toString()
fun ECKeyPair.dpk() = DumpedPrivateKey(this)
fun ECKeyPair.address() = this.public.address()
fun String.ecKey() = this.dpk().ecKey()

/**
* Method generates private key from phrase provided by parameter of type [String]. If parameter [normalize] is true, provided pass phrase will be converted
Expand Down
18 changes: 18 additions & 0 deletions library/src/main/java/ch/decent/sdk/model/AmountWithAsset.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package ch.decent.sdk.model

import java.text.NumberFormat

data class AmountWithAsset(
val asset: Asset,
val amount: AssetAmount
) : AssetFormatter by asset {

fun fromRaw() = fromRaw(amount.amount)

fun format(formatter: NumberFormat) = formatter.format(fromRaw(amount.amount)) + " $symbol"

fun format() = defaultFormatter.format(fromRaw(amount.amount)) + " $symbol"

fun format(formatter: NumberFormat.() -> Unit) = defaultFormatter.apply(formatter).format(fromRaw(amount.amount)) + " $symbol"

}
4 changes: 2 additions & 2 deletions library/src/main/java/ch/decent/sdk/model/AssetFormatter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ interface AssetFormatter {

fun format(value: BigDecimal) = defaultFormatter.format(value) + " $symbol"

fun format(value: BigDecimal, formatter: NumberFormat.() -> NumberFormat) = formatter(defaultFormatter).format(value) + " $symbol"
fun format(value: BigDecimal, formatter: NumberFormat.() -> Unit) = defaultFormatter.apply(formatter).format(value) + " $symbol"

/**
* format raw value with asset symbol
Expand All @@ -59,7 +59,7 @@ interface AssetFormatter {

fun format(value: BigInteger) = defaultFormatter.format(fromRaw(value)) + " $symbol"

fun format(value: BigInteger, formatter: NumberFormat.() -> NumberFormat) = formatter(defaultFormatter).format(fromRaw(value)) + " $symbol"
fun format(value: BigInteger, formatter: NumberFormat.() -> Unit) = defaultFormatter.apply(formatter).format(fromRaw(value)) + " $symbol"

fun amount(value: String): AssetAmount = AssetAmount(toRaw(BigDecimal(value)), id)
fun amount(value: Double): AssetAmount = AssetAmount(toRaw(BigDecimal(value)), id)
Expand Down
9 changes: 5 additions & 4 deletions library/src/main/java/ch/decent/sdk/model/OperationType.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ enum class OperationType(val clazz: Class<*>? = null) {
ASSERT_OPERATION,
CONTENT_SUBMIT_OPERATION(ContentSubmitOperation::class.java), //20
REQUEST_TO_BUY_OPERATION(PurchaseContentOperation::class.java),
LEAVE_RATING_AND_COMMENT_OPERATION,
LEAVE_RATING_AND_COMMENT_OPERATION(LeaveRatingAndCommentOperation::class.java),
READY_TO_PUBLISH_OPERATION,
PROOF_OF_CUSTODY_OPERATION,
DELIVER_KEYS_OPERATION, //25
Expand All @@ -44,10 +44,11 @@ enum class OperationType(val clazz: Class<*>? = null) {
UPDATE_MONITORED_ASSET_OPERATION,
READY_TO_PUBLISH2_OPERATION,
TRANSFER2_OPERATION(TransferOperation::class.java),
DISALLOW_AUTOMATIC_RENEWAL_OF_SUBSCRIPTION_OPERATION, // VIRTUAL 40
UPDATE_USER_ISSUED_ASSET_ADVANCED,
DISALLOW_AUTOMATIC_RENEWAL_OF_SUBSCRIPTION_OPERATION, // VIRTUAL 41
RETURN_ESCROW_SUBMISSION_OPERATION, // VIRTUAL
RETURN_ESCROW_BUYING_OPERATION, // VIRTUAL
PAY_SEEDER_OPERATION, // VIRTUAL
FINISH_BUYING_OPERATION, // VIRTUAL
RENEWAL_OF_SUBSCRIPTION_OPERATION // VIRTUAL 45
FINISH_BUYING_OPERATION, // VIRTUAL 45
RENEWAL_OF_SUBSCRIPTION_OPERATION // VIRTUAL
}
33 changes: 33 additions & 0 deletions library/src/main/java/ch/decent/sdk/model/Operations.kt
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,39 @@ class ContentSubmitOperation constructor(
}
}

/**
* Leave comment and rating operation constructor
*
* @param uri uri of the content
* @param consumer chain object id of the buyer's account
* @param rating 1-5 stars
* @param comment max 100 chars
* @param fee [AssetAmount] fee for the operation, if left [BaseOperation.FEE_UNSET] the fee will be computed in DCT asset
*/
class LeaveRatingAndCommentOperation constructor(
@SerializedName("URI") val uri: String,
@SerializedName("consumer") val consumer: ChainObject,
@SerializedName("rating") val rating: Int,
@SerializedName("comment") val comment: String,
fee: AssetAmount = BaseOperation.FEE_UNSET
) : BaseOperation(OperationType.LEAVE_RATING_AND_COMMENT_OPERATION, fee) {

init {
require(rating in 1..5) { "rating must be between 0-5" }
require(comment.length <= 100) { "comment max length is 100 chars" }
}

override val bytes: ByteArray
get() = Bytes.concat(
byteArrayOf(type.ordinal.toByte()),
fee.bytes,
uri.bytes(),
consumer.bytes,
comment.bytes(),
rating.toLong().bytes()
)
}

class SendMessageOperation constructor(
messagePayloadJson: String,
payer: ChainObject,
Expand Down
2 changes: 1 addition & 1 deletion library/src/main/java/ch/decent/sdk/model/Purchase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ data class Purchase(
@SerializedName("rating") val rating: BigInteger,
@SerializedName("comment") val comment: String,
@SerializedName("expiration_time") val expiration: LocalDateTime,
@SerializedName("pubKey") val pubElGamalKey: PubKey,
@SerializedName("pubKey") val pubElGamalKey: PubKey?,
@SerializedName("key_particles") val keyParticles: List<KeyParts>,
@SerializedName("expired") val expired: Boolean,
@SerializedName("delivered") val delivered: Boolean,
Expand Down
5 changes: 3 additions & 2 deletions library/src/main/java/ch/decent/sdk/model/TypeAdapters.kt
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,11 @@ object OperationTypeFactory : TypeAdapterFactory {
}

object PubKeyAdapter : TypeAdapter<PubKey>() {
override fun read(reader: JsonReader): PubKey {
override fun read(reader: JsonReader): PubKey? {
reader.beginObject()
reader.nextName()
val key = PubKey(BigInteger(reader.nextString().dropLast(1)))
val int = reader.nextString().dropLast(1)
val key = if (int.isBlank()) null else PubKey(BigInteger(int))
reader.endObject()
return key
}
Expand Down
Loading

0 comments on commit 100a7ad

Please sign in to comment.