Skip to content

Commit

Permalink
Merge pull request #245 from horizontalsystems/release
Browse files Browse the repository at this point in the history
Release 0.2.0
  • Loading branch information
omurovch authored Mar 1, 2019
2 parents 56758ce + af90314 commit 8bf3039
Show file tree
Hide file tree
Showing 39 changed files with 695 additions and 206 deletions.
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,11 @@ class MainViewModel : ViewModel(), BitcoinKit.Listener {
bitcoinKit.listener = this
}

override fun onTransactionsUpdate(bitcoinKit: BitcoinKit, inserted: List<TransactionInfo>, updated: List<TransactionInfo>, deleted: List<Int>) {
override fun onTransactionsUpdate(bitcoinKit: BitcoinKit, inserted: List<TransactionInfo>, updated: List<TransactionInfo>) {
// do something with transactions
}

override fun onTransactionsDelete(hashes: List<String>) {
// do something with transactions
}

Expand Down Expand Up @@ -197,6 +201,14 @@ class MainViewModel : ViewModel(), BitcoinKit.Listener {
}
```

Listener events are run in a dedicated background thread. It can be switched to main thread by setting the ```listenerExecutor``` property to ```MainThreadExecutor()```

```kotlin

bitcoinKit.listenerExecutor = MainThreadExecutor()

```

## Prerequisites
* JDK >= 1.8
* Android 6 (minSdkVersion 23) or greater
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.TextView
import io.horizontalsystems.bitcoinkit.BitcoinKit

class BalanceFragment : Fragment() {

lateinit var viewModel: MainViewModel
lateinit var networkName: TextView
lateinit var balanceValue: TextView
lateinit var lastBlockValue: TextView
lateinit var progressValue: TextView
lateinit var stateValue: TextView
lateinit var startButton: Button
lateinit var clearButton: Button
lateinit var buttonDebug: Button
Expand All @@ -38,9 +39,18 @@ class BalanceFragment : Fragment() {
lastBlockValue.text = (it ?: 0).toString()
})

viewModel.progress.observe(this, Observer {
val percentage = (it ?: 0.0) * 100
progressValue.text = "${percentage}%"
viewModel.state.observe(this, Observer {
when (it) {
is BitcoinKit.KitState.Synced -> {
stateValue.text = "synced"
}
is BitcoinKit.KitState.Syncing -> {
stateValue.text = "syncing ${it.progress}"
}
is BitcoinKit.KitState.NotSynced -> {
stateValue.text = "not synced"
}
}
})

viewModel.status.observe(this, Observer {
Expand Down Expand Up @@ -69,7 +79,7 @@ class BalanceFragment : Fragment() {

balanceValue = view.findViewById(R.id.balanceValue)
lastBlockValue = view.findViewById(R.id.lastBlockValue)
progressValue = view.findViewById(R.id.progressValue)
stateValue = view.findViewById(R.id.stateValue)
startButton = view.findViewById(R.id.buttonStart)
clearButton = view.findViewById(R.id.buttonClear)
buttonDebug = view.findViewById(R.id.buttonDebug)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class MainViewModel : ViewModel(), BitcoinKit.Listener {
val transactions = MutableLiveData<List<TransactionInfo>>()
val balance = MutableLiveData<Long>()
val lastBlockHeight = MutableLiveData<Int>()
val progress = MutableLiveData<Double>()
val state = MutableLiveData<KitState>()
val status = MutableLiveData<State>()
val networkName: String
private val disposables = CompositeDisposable()
Expand Down Expand Up @@ -47,7 +47,7 @@ class MainViewModel : ViewModel(), BitcoinKit.Listener {
}

lastBlockHeight.value = bitcoinKit.lastBlockInfo?.height ?: 0
progress.value = 0.0
state.value = KitState.NotSynced

started = false
}
Expand Down Expand Up @@ -82,33 +82,26 @@ class MainViewModel : ViewModel(), BitcoinKit.Listener {
//
// BitcoinKit Listener implementations
//
override fun onTransactionsUpdate(bitcoinKit: BitcoinKit, inserted: List<TransactionInfo>, updated: List<TransactionInfo>, deleted: List<Int>) {
override fun onTransactionsUpdate(bitcoinKit: BitcoinKit, inserted: List<TransactionInfo>, updated: List<TransactionInfo>) {
bitcoinKit.transactions().subscribe { txList: List<TransactionInfo> ->
transactions.value = txList.sortedByDescending { it.blockHeight }
transactions.postValue(txList.sortedByDescending { it.blockHeight })
}.let {
disposables.add(it)
}
}

override fun onTransactionsDelete(hashes: List<String>) {
}

override fun onBalanceUpdate(bitcoinKit: BitcoinKit, balance: Long) {
this.balance.value = balance
this.balance.postValue(balance)
}

override fun onLastBlockInfoUpdate(bitcoinKit: BitcoinKit, blockInfo: BlockInfo) {
this.lastBlockHeight.value = blockInfo.height
this.lastBlockHeight.postValue(blockInfo.height)
}

override fun onKitStateUpdate(bitcoinKit: BitcoinKit, state: KitState) {
when (state) {
is KitState.Synced -> {
this.progress.postValue(1.0)
}
is KitState.Syncing -> {
this.progress.postValue(state.progress)
}
is KitState.NotSynced -> {
this.progress.postValue(0.0)
}
}
this.state.postValue(state)
}
}
16 changes: 8 additions & 8 deletions app/src/main/res/layout/fragment_balance.xml
Original file line number Diff line number Diff line change
Expand Up @@ -46,34 +46,34 @@
tools:text="100.0" />

<TextView
android:id="@+id/progressTitle"
android:id="@+id/stateTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="Sync Progress: "
android:text="State:"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/balanceTitle" />

<TextView
android:id="@+id/progressValue"
android:id="@+id/stateValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
app:layout_constraintStart_toEndOf="@+id/progressTitle"
app:layout_constraintTop_toTopOf="@+id/progressTitle"
tools:text="12%" />
app:layout_constraintStart_toEndOf="@+id/stateTitle"
app:layout_constraintTop_toTopOf="@+id/stateTitle"
tools:text="syncing" />

<TextView
android:id="@+id/lastBlockTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="Last Block: "
android:text="Last Block:"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/progressTitle" />
app:layout_constraintTop_toBottomOf="@+id/stateTitle" />

<TextView
android:id="@+id/lastBlockValue"
Expand Down
3 changes: 1 addition & 2 deletions bitcoinkit/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ android {
minSdkVersion 23
targetSdkVersion 28
versionCode 1
versionName "0.1.2"
versionName "0.2.0"

testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
Expand Down Expand Up @@ -48,7 +48,6 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"

implementation 'io.reactivex.rxjava2:rxjava:2.2.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'

// For cryptocurrency
implementation 'com.madgag.spongycastle:core:1.58.0.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ class BlockchainTest {
private val factories = RealmFactoryMock()
private val realm = factories.realmFactory.realm
private val network = mock(Network::class.java)
private val blockchain = Blockchain(network)
private val blockchainDataListener = mock(IBlockchainDataListener::class.java)
private val blockchain = Blockchain(network, blockchainDataListener)

@Before
fun setup() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import com.nhaarman.mockito_kotlin.*
import helpers.RxTestRule
import io.horizontalsystems.bitcoinkit.RealmFactoryMock
import io.horizontalsystems.bitcoinkit.core.ISyncStateListener
import io.horizontalsystems.bitcoinkit.models.Block
import io.horizontalsystems.bitcoinkit.models.BlockHash
import io.horizontalsystems.bitcoinkit.models.PublicKey
import io.horizontalsystems.bitcoinkit.network.peer.PeerGroup
Expand All @@ -20,7 +19,7 @@ import java.util.concurrent.TimeUnit
class InitialSyncerTest {

private val factories = RealmFactoryMock()
private val initialSyncerApi = mock(InitialSyncerApi::class.java)
private val initialSyncerApi = mock(BlockDiscoveryBatch::class.java)
private val kitStateListener = mock(ISyncStateListener::class.java)

private val stateManager = mock(StateManager::class.java)
Expand Down Expand Up @@ -48,10 +47,10 @@ class InitialSyncerTest {
@Test
fun sync() {
whenever(stateManager.restored).thenReturn(false)
whenever(initialSyncerApi.fetchFromApi(0, true)).thenReturn(apiRespStub)
whenever(initialSyncerApi.fetchFromApi(0, false)).thenReturn(apiRespStub)
whenever(initialSyncerApi.fetchFromApi(1, true)).thenReturn(Single.just(Pair(listOf(), listOf())))
whenever(initialSyncerApi.fetchFromApi(1, false)).thenReturn(Single.just(Pair(listOf(), listOf())))
whenever(initialSyncerApi.discoverBlockHashes(0, true)).thenReturn(apiRespStub)
whenever(initialSyncerApi.discoverBlockHashes(0, false)).thenReturn(apiRespStub)
whenever(initialSyncerApi.discoverBlockHashes(1, true)).thenReturn(Single.just(Pair(listOf(), listOf())))
whenever(initialSyncerApi.discoverBlockHashes(1, false)).thenReturn(Single.just(Pair(listOf(), listOf())))

initialSyncer.sync()

Expand All @@ -65,8 +64,8 @@ class InitialSyncerTest {
val responseWithTimeout = apiRespStub.timeout(1, TimeUnit.SECONDS)

whenever(stateManager.restored).thenReturn(false)
whenever(initialSyncerApi.fetchFromApi(0, true)).thenReturn(responseWithTimeout)
whenever(initialSyncerApi.fetchFromApi(0, false)).thenReturn(responseWithTimeout)
whenever(initialSyncerApi.discoverBlockHashes(0, true)).thenReturn(responseWithTimeout)
whenever(initialSyncerApi.discoverBlockHashes(0, false)).thenReturn(responseWithTimeout)

initialSyncer.sync()
initialSyncer.stop()
Expand All @@ -77,8 +76,8 @@ class InitialSyncerTest {
// @Test
// fun refresh() {
// whenever(stateManager.restored).thenReturn(false)
// whenever(initialSyncerApi.fetchFromApi(true)).thenReturn(apiRespStub)
// whenever(initialSyncerApi.fetchFromApi(false)).thenReturn(apiRespStub)
// whenever(initialSyncerApi.discoverBlockHashes(true)).thenReturn(apiRespStub)
// whenever(initialSyncerApi.discoverBlockHashes(false)).thenReturn(apiRespStub)
//
// initialSyncer.sync()
// initialSyncer.sync() // refresh
Expand Down Expand Up @@ -130,10 +129,10 @@ class InitialSyncerTest {

whenever(stateManager.restored).thenReturn(false)

whenever(initialSyncerApi.fetchFromApi(0, true)).thenReturn(externalObservable)
whenever(initialSyncerApi.fetchFromApi(0, false)).thenReturn(internalObservable)
whenever(initialSyncerApi.fetchFromApi(1, true)).thenReturn(Single.just(Pair(listOf(), listOf())))
whenever(initialSyncerApi.fetchFromApi(1, false)).thenReturn(Single.just(Pair(listOf(), listOf())))
whenever(initialSyncerApi.discoverBlockHashes(0, true)).thenReturn(externalObservable)
whenever(initialSyncerApi.discoverBlockHashes(0, false)).thenReturn(internalObservable)
whenever(initialSyncerApi.discoverBlockHashes(1, true)).thenReturn(Single.just(Pair(listOf(), listOf())))
whenever(initialSyncerApi.discoverBlockHashes(1, false)).thenReturn(Single.just(Pair(listOf(), listOf())))

initialSyncer.sync()

Expand All @@ -156,16 +155,16 @@ class InitialSyncerTest {
fun sync_apiNotSynced_blocksDiscoveredFail() {
whenever(stateManager.restored).thenReturn(false)

whenever(initialSyncerApi.fetchFromApi(0, true)).thenReturn(Single.error(Exception()))
whenever(initialSyncerApi.fetchFromApi(0, false)).thenReturn(Single.error(Exception()))
whenever(initialSyncerApi.discoverBlockHashes(0, true)).thenReturn(Single.error(Exception()))
whenever(initialSyncerApi.discoverBlockHashes(0, false)).thenReturn(Single.error(Exception()))

initialSyncer.sync()

verify(stateManager, never()).restored = true
verifyNoMoreInteractions(peerGroup)

assertTrue(realm.where(PublicKey::class.java).findAll().isEmpty())
assertTrue(realm.where(Block::class.java).findAll().isEmpty())
assertTrue(realm.where(BlockHash::class.java).findAll().isEmpty())
}

private fun containsBlock(blocks: List<BlockHash>, block: BlockHash) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ import com.nhaarman.mockito_kotlin.verify
import com.nhaarman.mockito_kotlin.whenever
import helpers.Fixtures
import io.horizontalsystems.bitcoinkit.RealmFactoryMock
import io.horizontalsystems.bitcoinkit.models.Transaction
import io.horizontalsystems.bitcoinkit.network.peer.PeerGroup
import io.horizontalsystems.bitcoinkit.transactions.builder.TransactionBuilder
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.mockito.Mockito.mock
Expand Down Expand Up @@ -38,10 +36,7 @@ class TransactionCreatorTest {
fun create_Success() {
transactionCreator.create("address", 10_000_000, 8, true)

val insertedTx = realm.where(Transaction::class.java).equalTo("hashHexReversed", transactionP2PKH.hashHexReversed).findFirst()

assertTrue(insertedTx != null)
verify(transactionProcessor).process(transactionP2PKH, realm)
verify(transactionProcessor).processOutgoing(transactionP2PKH, realm)
verify(peerGroup).sendPendingTransactions()
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
package io.horizontalsystems.bitcoinkit.transactions

import com.nhaarman.mockito_kotlin.any
import com.nhaarman.mockito_kotlin.check
import com.nhaarman.mockito_kotlin.eq
import com.nhaarman.mockito_kotlin.whenever
import helpers.Fixtures
import io.horizontalsystems.bitcoinkit.RealmFactoryMock
import io.horizontalsystems.bitcoinkit.blocks.IBlockchainDataListener
import io.horizontalsystems.bitcoinkit.managers.AddressManager
import io.horizontalsystems.bitcoinkit.models.Transaction
import io.horizontalsystems.bitcoinkit.models.TransactionInput
import io.horizontalsystems.bitcoinkit.models.TransactionOutput
import io.realm.Realm
import org.junit.Assert
import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertEquals
import org.junit.Before
Expand All @@ -20,10 +24,11 @@ class TransactionProcessorTest {

private val factory = RealmFactoryMock()
private lateinit var realm: Realm
private val transaction = mock(Transaction::class.java)
private val transaction = Fixtures.transactionP2PKH
private val linker = mock(TransactionLinker::class.java)
private val extractor = mock(TransactionExtractor::class.java)
private val addressManager = mock(AddressManager::class.java)
private val blockchainDataListener = mock(IBlockchainDataListener::class.java)

private lateinit var processor: TransactionProcessor

Expand All @@ -33,22 +38,25 @@ class TransactionProcessorTest {
realm.executeTransaction {
it.deleteAll()
}
processor = TransactionProcessor(extractor, linker, addressManager)
processor = TransactionProcessor(extractor, linker, addressManager, blockchainDataListener)
}

@Test
fun process() {
whenever(transaction.isMine).thenReturn(false)
processor.process(transaction, realm)
processor.processOutgoing(transaction, realm)

verify(extractor).extractOutputs(transaction, realm)
verify(linker).handle(transaction, realm)
verify(blockchainDataListener).onTransactionsUpdate(check {
Assert.assertEquals(transaction.hashHexReversed, it.firstOrNull()?.hashHexReversed)
}, eq(listOf()))
}

@Test
fun process_isMine() {
whenever(transaction.isMine).thenReturn(true)
processor.process(transaction, realm)
transaction.isMine = true

processor.processOutgoing(transaction, realm)

verify(extractor).extractOutputs(transaction, realm)
verify(extractor).extractInputs(transaction)
Expand Down
Loading

0 comments on commit 8bf3039

Please sign in to comment.