Skip to content

Commit

Permalink
minter 1.2-ready, updated tests, updated readme
Browse files Browse the repository at this point in the history
  • Loading branch information
edwardstock committed Oct 16, 2020
1 parent 1b7bac5 commit 2e281a6
Show file tree
Hide file tree
Showing 75 changed files with 1,522 additions and 986 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@
gradle/wrapper/gradle-wrapper.jar
src/main/java/network/minter/blockchain/samples
src/test/java/network/minter/blockchain/repos/SendTest.java
src/test/java/network/minter/blockchain/utils/LocalTest.java
111 changes: 62 additions & 49 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ project build.gradle
```groovy
ext {
minterBlockchainSDK = "0.13.1"
minterBlockchainSDK = "1.0.0"
}
dependencies {
Expand All @@ -41,41 +41,48 @@ dependencies {

Use our nodes
```java
MinterBlockChainApi.initialize();
MinterBlockChainSDK.initialize();
```

Or it's HIGHLY RECOMMENDED to use you own node instead of Minter's.
```java
MinterBlockChainApi.initialize("https://your-node.local");
MinterBlockChainSDK.initialize("https://your-node.local");
```

### 2. Creating and signing transactions

Transactions API uses **Builder** pattern, so it so easy to handle it.

All transactions requires a valid **nonce** value. Nonce - is a number of transaction. To get valid transaction number, you should get current number via `BlockChainAccountRepository#getTransactionCount` and increment it:

```java
// init object with your Minter address
MinterAddress myAddress = new MinterAddress("Mxccc3fc91a3d47dc1ee26d62611a09831f0214d62");

// get account repository from SDK singleton object
BlockChainAccountRepository repo = MinterBlockChainApi.getInstance().account();

// send request
repo.getTransactionCount(myAddress).enqueue(new Callback<BCResult<CountableData>>() {
@Override
public void onResponse(Call<BCResult<CountableData>> call, Response<BCResult<CountableData>> response) {
BigInteger txCount = response.body().result.count;

// use this incremented value as nonce to your transaction
BigInteger nonce = txCount.add(new BigInteger("1"));
import io.reactivex.Scheduler;
import io.reactivex.schedulers.Schedulers;
import network.minter.blockchain.MinterBlockChainSDK;
import network.minter.blockchain.repo.NodeTransactionRepository;
class MyClass {

void myMethod() {
Transaction tx = new Transaction.Builder(new BigInteger("1"))
.setBlockchainId(BlockchainID.MainNet)
.setGasCoinId(DEFAULT_COIN_ID)
.sendCoin()
.setCoinId(DEFAULT_COIN_ID)
.setValue("0.012345")
.setTo(toAddress)
.build();

TransactionSign sign = tx.signSingle(privateKey);

NodeTransactionRepository txRepo = MinterBlockChainSDK.getInstance().transactions();
txRepo.sendTransaction(sign)
.observeOn(Schedulers.io())
.subscribeOn(Scheduler.io())
.subscribe(sendResult -> {
System.out.println(sendResult.txHash.toString());
}, throwable -> {
// handle error
});
}

@Override
public void onFailure(Call<BCResult<CountableData>> call, Throwable t) {
}
})
}
```

#### 2.1 Create "Send" transaction
Expand All @@ -98,14 +105,16 @@ final PrivateKey privateKey = PrivateKey.fromMnemonic("your phrase must contains
Create transaction builder and build transaction:
```java
Transaction tx = new Transaction.Builder(nonce)
// by default it depends on what sdk build type you used: with or without suffix "-testnet"
.setBlockchainId(BlockchainID.MainNet)
// optional: available for all transactions, but not useful for some transactions
.setGasCoin("MNT")
.setGasCoinId(DEFAULT_COIN_ID)
// here you should select what transaction you are trying to create, builder will select exact type
.sendCoin()
// required: coin to send
.setCoin(coin)
// required: value to send
.setValue("10")
// required: coin to send represented by it's ID
.setCoinId(DEFAULT_COIN_ID)
// value to send
.setValue("0.012345")
// required: recipient address
.setTo(toAddress)
// finally, build object
Expand All @@ -115,40 +124,33 @@ Transaction tx = new Transaction.Builder(nonce)
Sign transaction using your private key
```java
TransactionSign sign = tx.sign(privateKey);

// get transaction hash - this hash you'll send to blockchain
String signedTransaction = sign.getTxSign();
```


So, it's easy, isn't? :)

For more transaction types see `OperationType` and class `Transaction.Builder`

Now we'll send transaction to blockchain.

#### 2.2 Send "send" transaction to the Minter blockchain

To send transaction to blockchain, we need to get `BlockChainAccountRepository` from `MinterBlockChainApi`
To send transaction to blockchain, we need to get `NodeTransactionRepository` from `MinterBlockChainSDK`

```java
BlockChainAccountRepository accountRepo = MinterBlockChainApi.getInstance().account();
NodeTransactionRepository accountRepo = MinterBlockChainSDK.getInstance().transactions();
```

To send transaction, you just need to call http request
```java
TransactionSign sign = ...
accountRepo.sendTransaction(sign).enqueue(new Callback<BCResult<TransactionSendResult>>() {
@Override
public void onResponse(Call<BCResult<TransactionSendResult>> call, Response<BCResult<TransactionSendResult>> response) {
// handle send result
}

@Override
public void onFailure(Call<BCResult<TransactionSendResult>> call, Throwable t) {
// handle send error
}
})
NodeTransactionRepository txRepo = MinterBlockChainSDK.getInstance().transactions();
txRepo.sendTransaction(sign)
.observeOn(Schedulers.io())
.subscribeOn(Scheduler.io())
.subscribe(sendResult -> {
System.out.println(sendResult.txHash.toString());
}, throwable -> {
// handle error
});
```

That's all!
Expand All @@ -159,10 +161,21 @@ That's all!
Javadoc available in code and in *.jar file at the bintray

## Build
TODO
To create local artifacts that you can find in your home `~/.m2` directory, just run:
```bash
bash project_root/publish_local.sh
```

## Tests
TODO

To run unit tests, you must build bip39 and secp256k1 with host target
See: [bip39](https://github.com/edwardstock/bip3x) and [secp256k1-java](https://github.com/edwardstock/native-secp256k1-java)

All these test can be runned only with testnet configuration, don't use `gradlew test` directly
```bash
cd project_root
./gradlew testNetTestDebugUnitTest -PnativeLibPath=/path/to/native/libs
```

## Changelog

Expand Down
10 changes: 7 additions & 3 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
# Release notes

## 1.0.0
- **BREAKING CHANGES**
- Now communication with node api works with RxJava3
- Base API response now in root of json, so BcResult<T> is just NodeResult and each response object inherits this class
- **BREAKING CHANGES**
- Renamed `MinterBlockChainApi` to `MinterBlockChainSDK`
- Now communication with node api works with RxJava2
- Base API response now in root of json, so BcResult<T> is just NodeResult and each response object inherits this class
- Added `synchronized` blocks to methods uses native secp256k1 context
- `CheckTransaction#sign()` now returns `MinterCheck` instead of `TransactionSign`
- Added `CheckTransaction#validatePassword()` method to verify check password validity offline


## 0.13.1
Expand Down
28 changes: 18 additions & 10 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ buildscript {
mavenLocal()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.0.1'
classpath 'com.android.tools.build:gradle:4.1.0'
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.5'
}
}
Expand Down Expand Up @@ -61,9 +61,8 @@ version = '1.0.0'

ext {
minterMinSdk = 16
minterMaxSdk = 29
minterBuildTools = "29.0.3"
minterLibSupport = "28.0.0"
minterMaxSdk = 30
minterBuildTools = "30.0.2"
minterCoreVers = "1.0.0"


Expand Down Expand Up @@ -168,15 +167,23 @@ android {
// can't use name starts with 'test'
netMain {
dimension "env"
buildConfigField "String", "BASE_NODE_URL", '"https://minter-node.apps.minter.network"'
buildConfigField "String", "BASE_NODE_URL", '"https://minter-node.apps.minter.network/"'
buildConfigField "String", "BASE_NODE_VERSION", '"v2"'
buildConfigField "network.minter.blockchain.models.operational.BlockchainID", "BLOCKCHAIN_ID", "network.minter.blockchain.models.operational.BlockchainID.MainNet"
}
netTest {
dimension "env"
buildConfigField "String", "BASE_NODE_URL", '"https://minter-node-1.testnet.minter.network"'
buildConfigField "String", "BASE_NODE_URL", '"https://node-api.testnet.minter.network/"'
buildConfigField "String", "BASE_NODE_VERSION", '"v2"'
buildConfigField "network.minter.blockchain.models.operational.BlockchainID", "BLOCKCHAIN_ID", "network.minter.blockchain.models.operational.BlockchainID.TestNet"
buildConfigField "String", "QA_MNEMONIC", getProperty("minter_qa_mnemonic")
buildConfigField "String", "TESTNET_MNEMONIC", getProperty("minter_testnet_mnemonic")
}
}

libraryVariants.all { variant ->
variant.buildConfigField "String", "VERSION_NAME", '"' + version + '"'
}
}

dependencies {
Expand All @@ -188,12 +195,13 @@ dependencies {
compileOnly 'com.google.code.findbugs:jsr305:3.0.2'

// network and rxjava
implementation 'io.reactivex.rxjava2:rxjava:2.2.17'

implementation 'com.squareup.okhttp3:okhttp:4.8.1'
implementation 'com.squareup.okhttp3:logging-interceptor:4.8.1'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'io.reactivex.rxjava3:rxjava:3.0.5'
implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.9.0'

implementation 'org.parceler:parceler-api:1.1.13'
annotationProcessor 'org.parceler:parceler:1.1.13'
Expand All @@ -206,8 +214,8 @@ dependencies {
// testing
testImplementation 'junit:junit:4.13'
androidTestImplementation 'androidx.annotation:annotation:1.1.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test:rules:1.2.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test:rules:1.3.0'
androidTestImplementation 'com.squareup.retrofit2:converter-gson:2.9.0'
androidTestImplementation 'com.google.code.gson:gson:2.8.6'
}
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
android.enableJetifier=true
android.enableJetifier=false
android.useAndroidX=true
bintray_user=edwardstock
bintray_license_name=MIT
Expand Down
4 changes: 2 additions & 2 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@
# THE SOFTWARE.
#

#Wed Feb 26 14:06:05 MSK 2020
#Fri Oct 16 17:29:05 MSK 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
49 changes: 34 additions & 15 deletions src/main/java/network/minter/blockchain/MinterBlockChainSDK.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,14 @@

import com.google.gson.GsonBuilder;

import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.math.BigInteger;

import javax.annotation.Nonnull;

import io.reactivex.rxjava3.schedulers.Schedulers;
import io.reactivex.schedulers.Schedulers;
import network.minter.blockchain.repo.NodeAddressRepository;
import network.minter.blockchain.repo.NodeBlockRepository;
import network.minter.blockchain.repo.NodeCoinRepository;
Expand All @@ -53,18 +56,20 @@
import network.minter.core.internal.common.Acceptor;
import network.minter.core.internal.log.Mint;
import network.minter.core.internal.log.TimberLogger;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;


/**
* minter-android-blockchain. 2018
* @author Eduard Maximovich <[email protected]>
*/
public class MinterBlockChainSDK {
private final static String BASE_NODE_URL = BuildConfig.BASE_NODE_URL;
private final static String BASE_NODE_URL = BuildConfig.BASE_NODE_URL + BuildConfig.BASE_NODE_VERSION + "/";
private static MinterBlockChainSDK INSTANCE;
private final ApiService.Builder mApiService;
private NodeAddressRepository mAccountRepository;
Expand All @@ -84,23 +89,13 @@ private MinterBlockChainSDK(@Nonnull String baseNodeApiUrl) {
mApiService.setRetrofitClientConfig(new Acceptor<Retrofit.Builder>() {
@Override
public void accept(Retrofit.Builder builder) {
builder.addCallAdapterFactory(RxJava3CallAdapterFactory.createWithScheduler(Schedulers.io()));
builder.addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()));
}
});
mApiService.addHeader("Content-Type", "application/json");
mApiService.addHeader("X-Minter-Client-Name", "MinterAndroid");
mApiService.addHeader("X-Minter-Client-Version", BuildConfig.VERSION_NAME);
mApiService.addHttpInterceptor(chain -> {
Request request = chain.request();
Response response = chain.proceed(request);
if (response.body() != null && response.body().contentType() != null && response.body().contentType().toString().toLowerCase().equals("application/json")) {
Response.Builder b = response.newBuilder();
b.code(200);

return b.build();
}
return response;
});
mApiService.addHttpInterceptor(new ResponseErrorToResultInterceptor());
}

public static void initialize() {
Expand Down Expand Up @@ -146,6 +141,10 @@ public static void initialize(boolean debug) {
initialize(BASE_NODE_URL, debug, new TimberLogger());
}

public static void initialize(boolean debug, Mint.Leaf logger) {
initialize(BASE_NODE_URL, debug, logger);
}

public static void initialize(String baseNodeApiUrl) {
initialize(baseNodeApiUrl, false, new TimberLogger());
}
Expand Down Expand Up @@ -227,4 +226,24 @@ public NodeCoinRepository coin() {

return mCoinRepository;
}

/**
* This class convert any HTTP error that contains valid json response to successful NodeResult response.
* It was made to help handle error messages from service and avoid manual exception extraction error body and un-json it
*/
public static class ResponseErrorToResultInterceptor implements Interceptor {
@NotNull
@Override
public Response intercept(@NotNull Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
if (response.body() != null && response.body().contentType() != null && response.body().contentType().toString().toLowerCase().startsWith("application/json")) {
Response.Builder b = response.newBuilder();
b.code(200);

return b.build();
}
return response;
}
}
}
Loading

0 comments on commit 2e281a6

Please sign in to comment.