Create synchronizer and add layer of business logic onto the data layer
App is functional and displays transactions
This commit is contained in:
parent
1501f1a7d2
commit
6c869a47df
64
build.gradle
64
build.gradle
|
@ -5,8 +5,10 @@ buildscript {
|
||||||
'targetSdkVersion': 28
|
'targetSdkVersion': 28
|
||||||
]
|
]
|
||||||
ext.versions = [
|
ext.versions = [
|
||||||
|
'architectureComponents': '2.0.0',
|
||||||
|
'grpc':'1.17.1',
|
||||||
'kotlin': '1.3.10',
|
'kotlin': '1.3.10',
|
||||||
'architectureComponents': '2.0.0'
|
'coroutines': '1.1.0'
|
||||||
]
|
]
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
|
@ -17,6 +19,7 @@ buildscript {
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
|
||||||
classpath "com.github.ben-manes:gradle-versions-plugin:0.20.0"
|
classpath "com.github.ben-manes:gradle-versions-plugin:0.20.0"
|
||||||
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
|
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
|
||||||
|
classpath "com.google.protobuf:protobuf-gradle-plugin:0.8.7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +28,7 @@ apply plugin: 'com.android.library'
|
||||||
apply plugin: "kotlin-android-extensions"
|
apply plugin: "kotlin-android-extensions"
|
||||||
apply plugin: "kotlin-android"
|
apply plugin: "kotlin-android"
|
||||||
apply plugin: 'kotlin-kapt'
|
apply plugin: 'kotlin-kapt'
|
||||||
|
apply plugin: 'com.google.protobuf'
|
||||||
apply plugin: 'com.github.ben-manes.versions'
|
apply plugin: 'com.github.ben-manes.versions'
|
||||||
apply plugin: 'com.github.dcendents.android-maven'
|
apply plugin: 'com.github.dcendents.android-maven'
|
||||||
|
|
||||||
|
@ -34,6 +38,7 @@ version = '1.2.4'
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
jcenter()
|
||||||
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
@ -42,9 +47,10 @@ android {
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 28
|
targetSdkVersion 28
|
||||||
versionCode = 1_02_03
|
versionCode = 1_03_00
|
||||||
versionName = "1.2.3"
|
versionName = "1.3.0"
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
multiDexEnabled false
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
@ -56,7 +62,10 @@ android {
|
||||||
sourceSets {
|
sourceSets {
|
||||||
main {
|
main {
|
||||||
java {
|
java {
|
||||||
srcDirs "build/generated/source/wire"
|
srcDirs "build/generated/source/grpc"
|
||||||
|
}
|
||||||
|
proto {
|
||||||
|
srcDir 'src/main/proto'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,29 +75,60 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clean {
|
||||||
|
delete "$project.projectDir/src/generated/source/grpc"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protobuf {
|
||||||
|
generatedFilesBaseDir = "$projectDir/src/generated/source/grpc"
|
||||||
|
protoc { artifact = 'com.google.protobuf:protoc:3.6.1' }
|
||||||
|
plugins {
|
||||||
|
javalite { artifact = "com.google.protobuf:protoc-gen-javalite:3.0.0" }
|
||||||
|
grpc { artifact = "io.grpc:protoc-gen-grpc-java:${versions.grpc}" }
|
||||||
|
}
|
||||||
|
generateProtoTasks {
|
||||||
|
all().each { task ->
|
||||||
|
task.plugins {
|
||||||
|
javalite {}
|
||||||
|
grpc { // Options added to --grpc_out
|
||||||
|
option 'lite' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// Square
|
implementation 'androidx.appcompat:appcompat:1.0.2'
|
||||||
api "com.squareup.wire:wire-runtime:2.2.0"
|
implementation 'androidx.multidex:multidex:2.0.1'
|
||||||
|
|
||||||
// Architecture components
|
// Architecture components
|
||||||
implementation "androidx.lifecycle:lifecycle-runtime:${versions.architectureComponents}"
|
implementation "androidx.lifecycle:lifecycle-runtime:${versions.architectureComponents}"
|
||||||
implementation "androidx.lifecycle:lifecycle-extensions:${versions.architectureComponents}"
|
implementation "androidx.lifecycle:lifecycle-extensions:${versions.architectureComponents}"
|
||||||
implementation "androidx.room:room-runtime:${versions.architectureComponents}"
|
implementation "androidx.room:room-runtime:${versions.architectureComponents}"
|
||||||
|
implementation "androidx.room:room-common:${versions.architectureComponents}"
|
||||||
kapt "androidx.lifecycle:lifecycle-compiler:${versions.architectureComponents}"
|
kapt "androidx.lifecycle:lifecycle-compiler:${versions.architectureComponents}"
|
||||||
kapt "androidx.room:room-compiler:${versions.architectureComponents}"
|
kapt "androidx.room:room-compiler:${versions.architectureComponents}"
|
||||||
|
|
||||||
// Other
|
// kotlin
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${versions.kotlin}"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${versions.kotlin}"
|
||||||
implementation "com.android.support:appcompat-v7:28.0.0"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions.coroutines}"
|
||||||
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:${versions.coroutines}"
|
||||||
|
|
||||||
|
// grpc-java
|
||||||
|
implementation "io.grpc:grpc-okhttp:${versions.grpc}"
|
||||||
|
implementation "io.grpc:grpc-protobuf-lite:${versions.grpc}"
|
||||||
|
implementation "io.grpc:grpc-stub:${versions.grpc}"
|
||||||
|
implementation 'javax.annotation:javax.annotation-api:1.2'
|
||||||
|
|
||||||
// Tests
|
// Tests
|
||||||
testImplementation "junit:junit:4.12"
|
testImplementation "junit:junit:4.12"
|
||||||
androidTestImplementation "androidx.test:runner:1.1.0"
|
androidTestImplementation "androidx.test:runner:1.1.1"
|
||||||
androidTestImplementation "androidx.test.espresso:espresso-core:3.1.0"
|
androidTestImplementation "androidx.test.espresso:espresso-core:3.1.1"
|
||||||
androidTestImplementation "androidx.test:core:1.0.0"
|
androidTestImplementation "androidx.test:core:1.1.0"
|
||||||
androidTestImplementation "androidx.arch.core:core-testing:${versions.architectureComponents}"
|
androidTestImplementation "androidx.arch.core:core-testing:${versions.architectureComponents}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
preBuild.dependsOn generateProtobufClasses
|
preBuild.dependsOn includeDirBugFix
|
||||||
preBuild.dependsOn copyAllJniLibs
|
preBuild.dependsOn copyAllJniLibs
|
|
@ -1,5 +1,6 @@
|
||||||
def protoSrcDir = "src/main/proto"
|
//def protoSrcDir = "src/main/proto"
|
||||||
def protoDestDir = "build/generated/source/wire"
|
//def protoDestDir = "build/generated/source/grpc"
|
||||||
|
def protoIncludeDir = "build/extracted-include-protos/main"
|
||||||
def jniSrcDir = "src/main/rust"
|
def jniSrcDir = "src/main/rust"
|
||||||
def jniDestDir = "build/rust/target"
|
def jniDestDir = "build/rust/target"
|
||||||
|
|
||||||
|
@ -11,36 +12,64 @@ def libArm64Dir = "src/main/jniLibs/arm64-v8a"
|
||||||
def libArmeabiFile = "build/rust/target/armv7-linux-androideabi/release/$libFile"
|
def libArmeabiFile = "build/rust/target/armv7-linux-androideabi/release/$libFile"
|
||||||
def libArmeabiDir = "src/main/jniLibs/armeabi-v7a"
|
def libArmeabiDir = "src/main/jniLibs/armeabi-v7a"
|
||||||
|
|
||||||
buildscript {
|
//buildscript {
|
||||||
dependencies {
|
// dependencies {
|
||||||
classpath("com.squareup.wire:wire-compiler:2.2.0")
|
// classpath("com.squareup.wire:wire-compiler:2.2.0")
|
||||||
}
|
// }
|
||||||
repositories {
|
// repositories {
|
||||||
mavenCentral()
|
// mavenCentral()
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
tasks.register("generateProtobufClasses") {
|
//tasks.register("generateGrpcClasses") {
|
||||||
|
// doFirst {
|
||||||
|
// println("**** CREATING PROTOS ****")
|
||||||
|
// delete(protoDestDir)
|
||||||
|
// mkdir(protoDestDir)
|
||||||
|
// }
|
||||||
|
// description = "Generate Java classes from protocol buffer (.proto) schema files for use with grpc"
|
||||||
|
//
|
||||||
|
// fileTree(dir: protoSrcDir, include: '**/*.proto').each { File file ->
|
||||||
|
// doLast {
|
||||||
|
// javaexec {
|
||||||
|
// main = "com.squareup.wire.WireCompiler"
|
||||||
|
// classpath = buildscript.configurations.classpath
|
||||||
|
// args = ["--proto_path=$protoSrcDir", "--java_out=$protoDestDir", file.path]
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// inputs.files(fileTree(dir: protoSrcDir, include: '**/*.proto'))
|
||||||
|
// outputs.files(fileTree(dir: protoDestDir, include: '**'))
|
||||||
|
//}
|
||||||
|
|
||||||
|
//tasks.register("generateProtobufClasses") {
|
||||||
|
// doFirst {
|
||||||
|
// println("**** CREATING PROTOS ****")
|
||||||
|
// delete(protoDestDir)
|
||||||
|
// mkdir(protoDestDir)
|
||||||
|
// }
|
||||||
|
// description = "Generate Java classes from protocol buffer (.proto) schema files for use with Square's Wire library"
|
||||||
|
//
|
||||||
|
// fileTree(dir: protoSrcDir, include: '**/*.proto').each { File file ->
|
||||||
|
// doLast {
|
||||||
|
// javaexec {
|
||||||
|
// main = "com.squareup.wire.WireCompiler"
|
||||||
|
// classpath = buildscript.configurations.classpath
|
||||||
|
// args = ["--proto_path=$protoSrcDir", "--java_out=$protoDestDir", file.path]
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// inputs.files(fileTree(dir: protoSrcDir, include: '**/*.proto'))
|
||||||
|
// outputs.files(fileTree(dir: protoDestDir, include: '**'))
|
||||||
|
//}
|
||||||
|
tasks.register("includeDirBugFix") {
|
||||||
doFirst {
|
doFirst {
|
||||||
println("**** CREATING PROTOS ****")
|
mkdir(protoIncludeDir)
|
||||||
delete(protoDestDir)
|
|
||||||
mkdir(protoDestDir)
|
|
||||||
}
|
}
|
||||||
description = "Generate Java classes from protocol buffer (.proto) schema files for use with Square's Wire library"
|
|
||||||
|
|
||||||
fileTree(dir: protoSrcDir, include: '**/*.proto').each { File file ->
|
|
||||||
doLast {
|
|
||||||
javaexec {
|
|
||||||
main = "com.squareup.wire.WireCompiler"
|
|
||||||
classpath = buildscript.configurations.classpath
|
|
||||||
args = ["--proto_path=$protoSrcDir", "--java_out=$protoDestDir", file.path]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
inputs.files(fileTree(dir: protoSrcDir, include: '**/*.proto'))
|
|
||||||
outputs.files(fileTree(dir: protoDestDir, include: '**'))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: run these in paralell with the worker API: https://guides.gradle.org/using-the-worker-api/
|
||||||
|
// note: this will require modifying the build script and having 3 separate calls
|
||||||
tasks.register("generateJniLibs") {
|
tasks.register("generateJniLibs") {
|
||||||
doFirst {
|
doFirst {
|
||||||
println("**** CREATING JNI LIBS ****")
|
println("**** CREATING JNI LIBS ****")
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
package cash.z.wallet.sdk.data
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import org.junit.AfterClass
|
||||||
|
import org.junit.Assert.assertNotNull
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.BeforeClass
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import rpc.CompactFormats
|
||||||
|
|
||||||
|
class CompactBlockDownloaderTest {
|
||||||
|
@get:Rule
|
||||||
|
var instantTaskExecutorRule = InstantTaskExecutorRule()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSynchronizerExists() {
|
||||||
|
assertNotNull(downloader)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testBlocks() = runBlocking {
|
||||||
|
msg("about to receive (is the channel Closed? ${downloader.blocks().isClosedForReceive})")
|
||||||
|
val result = downloader.blocks().receive()
|
||||||
|
msg("donezo")
|
||||||
|
assertTrue(printFailure(result), result.isSuccess)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun printFailure(result: Result<CompactFormats.CompactBlock>): String {
|
||||||
|
return if (result.isFailure) "result failed due to: ${result.exceptionOrNull()!!.let { "$it caused by: ${it.cause}" }}}"
|
||||||
|
else "success"
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testBlockHeight() = runBlocking {
|
||||||
|
delay(200)
|
||||||
|
val result = downloader.blocks().receive()
|
||||||
|
assertTrue(printFailure(result), result.isSuccess)
|
||||||
|
assertTrue("Unexpected height value", result.getOrThrow().height > 300000)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val job = Job()
|
||||||
|
val testScope = CoroutineScope(Dispatchers.IO + job)
|
||||||
|
val downloader = CompactBlockDownloader(testScope)
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
@JvmStatic
|
||||||
|
fun setup() {
|
||||||
|
downloader.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
@JvmStatic
|
||||||
|
fun close() {
|
||||||
|
downloader.stop()
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun msg(message: String) {
|
||||||
|
Log.e("DBUG", "[${Thread.currentThread().name}] $message")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package cash.z.wallet.sdk.data
|
||||||
|
|
||||||
|
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||||
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.cancel
|
||||||
|
import org.junit.AfterClass
|
||||||
|
import org.junit.Assert.assertNotNull
|
||||||
|
import org.junit.BeforeClass
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import rpc.CompactFormats
|
||||||
|
|
||||||
|
class SynchronizerTest {
|
||||||
|
@get:Rule
|
||||||
|
var instantTaskExecutorRule = InstantTaskExecutorRule()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSynchronizerExists() {
|
||||||
|
assertNotNull(synchronizer)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testBlockSaving() {
|
||||||
|
// synchronizer.saveBlocks()
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
fun testBlockScanning() {
|
||||||
|
Thread.sleep(180000L)
|
||||||
|
}
|
||||||
|
private fun printFailure(result: Result<CompactFormats.CompactBlock>): String {
|
||||||
|
return if (result.isFailure) "result failed due to: ${result.exceptionOrNull()!!.let { "$it caused by: ${it.cause}" }}}"
|
||||||
|
else "success"
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val job = Job()
|
||||||
|
val testScope = CoroutineScope(Dispatchers.IO + job)
|
||||||
|
val synchronizer = Synchronizer(ApplicationProvider.getApplicationContext(), testScope)
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
@JvmStatic
|
||||||
|
fun setup() {
|
||||||
|
synchronizer.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
@JvmStatic
|
||||||
|
fun close() {
|
||||||
|
synchronizer.stop()
|
||||||
|
testScope.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
package cash.z.wallet.sdk.db
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||||
|
import androidx.room.Room
|
||||||
|
import androidx.room.RoomDatabase
|
||||||
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
import cash.z.wallet.sdk.dao.BlockDao
|
||||||
|
import cash.z.wallet.sdk.dao.CompactBlockDao
|
||||||
|
import cash.z.wallet.sdk.dao.NoteDao
|
||||||
|
import cash.z.wallet.sdk.dao.TransactionDao
|
||||||
|
import cash.z.wallet.sdk.ext.toBlockHeight
|
||||||
|
import cash.z.wallet.sdk.jni.JniConverter
|
||||||
|
import cash.z.wallet.sdk.vo.CompactBlock
|
||||||
|
import io.grpc.ManagedChannel
|
||||||
|
import io.grpc.ManagedChannelBuilder
|
||||||
|
import org.junit.*
|
||||||
|
import org.junit.Assert.*
|
||||||
|
import rpc.CompactTxStreamerGrpc
|
||||||
|
import rpc.Service
|
||||||
|
import rpc.Service.*
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
class GlueIntegrationTest {
|
||||||
|
@get:Rule
|
||||||
|
var instantTaskExecutorRule = InstantTaskExecutorRule()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testDbExists() {
|
||||||
|
Log.e("tezt", "addData")
|
||||||
|
addData()
|
||||||
|
Log.e("tezt", "scanData")
|
||||||
|
scanData()
|
||||||
|
Log.e("tezt", "checkResults")
|
||||||
|
checkResults()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkResults() {
|
||||||
|
Thread.sleep(15000L)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addData() {
|
||||||
|
val result = blockingStub.getBlockRange(
|
||||||
|
BlockRange.newBuilder()
|
||||||
|
.setStart(373070L.toBlockHeight())
|
||||||
|
.setEnd(373085L.toBlockHeight())
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
while (result.hasNext()) {
|
||||||
|
val compactBlock = result.next()
|
||||||
|
dao.insert(CompactBlock(compactBlock.height.toInt(), compactBlock.toByteArray()))
|
||||||
|
System.err.println("stored block at height: ${compactBlock.height}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun scanData() {
|
||||||
|
Log.e("tezt", "scanning blocks...")
|
||||||
|
val result = converter.scanBlocks(
|
||||||
|
cacheDbPath,
|
||||||
|
"/data/user/0/cash.z.wallet.sdk.test/databases/data-glue.db",
|
||||||
|
"dummyseed".toByteArray(),
|
||||||
|
373070
|
||||||
|
)
|
||||||
|
System.err.println("done.")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun heightOf(height: Long): Service.BlockID {
|
||||||
|
return BlockID.newBuilder().setHeight(height).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
// jni
|
||||||
|
val converter: JniConverter = JniConverter()
|
||||||
|
|
||||||
|
// db
|
||||||
|
private lateinit var dao: CompactBlockDao
|
||||||
|
private lateinit var db: CompactBlockDb
|
||||||
|
private const val cacheDbName = "dummy-cache-glue.db"
|
||||||
|
private const val cacheDbPath = "/data/user/0/cash.z.wallet.sdk.test/databases/$cacheDbName"
|
||||||
|
|
||||||
|
// grpc
|
||||||
|
lateinit var blockingStub: CompactTxStreamerGrpc.CompactTxStreamerBlockingStub
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
@JvmStatic
|
||||||
|
fun setup() {
|
||||||
|
converter.initLogs()
|
||||||
|
|
||||||
|
val channel = ManagedChannelBuilder.forAddress("10.0.2.2", 9067).usePlaintext().build()
|
||||||
|
blockingStub = CompactTxStreamerGrpc.newBlockingStub(channel)
|
||||||
|
|
||||||
|
db = Room
|
||||||
|
.databaseBuilder(
|
||||||
|
ApplicationProvider.getApplicationContext(),
|
||||||
|
CompactBlockDb::class.java,
|
||||||
|
cacheDbName
|
||||||
|
)
|
||||||
|
.setJournalMode(RoomDatabase.JournalMode.TRUNCATE)
|
||||||
|
.fallbackToDestructiveMigration()
|
||||||
|
.build()
|
||||||
|
.apply { dao = complactBlockDao() }
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
@JvmStatic
|
||||||
|
fun close() {
|
||||||
|
db.close()
|
||||||
|
(blockingStub.channel as ManagedChannel).shutdown().awaitTermination(2000L, TimeUnit.MILLISECONDS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
package cash.z.wallet.sdk.db
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||||
|
import androidx.room.Room
|
||||||
|
import androidx.room.RoomDatabase
|
||||||
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
import cash.z.wallet.sdk.dao.BlockDao
|
||||||
|
import cash.z.wallet.sdk.dao.CompactBlockDao
|
||||||
|
import cash.z.wallet.sdk.dao.NoteDao
|
||||||
|
import cash.z.wallet.sdk.dao.TransactionDao
|
||||||
|
import cash.z.wallet.sdk.ext.toBlockHeight
|
||||||
|
import cash.z.wallet.sdk.jni.JniConverter
|
||||||
|
import cash.z.wallet.sdk.vo.CompactBlock
|
||||||
|
import io.grpc.ManagedChannel
|
||||||
|
import io.grpc.ManagedChannelBuilder
|
||||||
|
import org.junit.*
|
||||||
|
import org.junit.Assert.*
|
||||||
|
import rpc.CompactTxStreamerGrpc
|
||||||
|
import rpc.Service
|
||||||
|
import rpc.Service.*
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
class GlueSetupIntegrationTest {
|
||||||
|
@get:Rule
|
||||||
|
var instantTaskExecutorRule = InstantTaskExecutorRule()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testDbExists() {
|
||||||
|
assertNotNull(db)
|
||||||
|
// Log.e("tezt", "addData")
|
||||||
|
// addData()
|
||||||
|
// Log.e("tezt", "scanData")
|
||||||
|
// scanData()
|
||||||
|
// Log.e("tezt", "checkResults")
|
||||||
|
// checkResults()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkResults() {
|
||||||
|
Thread.sleep(15000L)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addData() {
|
||||||
|
val result = blockingStub.getBlockRange(
|
||||||
|
BlockRange.newBuilder()
|
||||||
|
.setStart(373070L.toBlockHeight())
|
||||||
|
.setEnd(373085L.toBlockHeight())
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
while (result.hasNext()) {
|
||||||
|
val compactBlock = result.next()
|
||||||
|
dao.insert(CompactBlock(compactBlock.height.toInt(), compactBlock.toByteArray()))
|
||||||
|
System.err.println("stored block at height: ${compactBlock.height}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun scanData() {
|
||||||
|
Log.e("tezt", "scanning blocks...")
|
||||||
|
val result = converter.scanBlocks(
|
||||||
|
cacheDbPath,
|
||||||
|
"/data/user/0/cash.z.wallet.sdk.test/databases/data-glue.db",
|
||||||
|
"dummyseed".toByteArray(),
|
||||||
|
373070
|
||||||
|
)
|
||||||
|
System.err.println("done.")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun heightOf(height: Long): Service.BlockID {
|
||||||
|
return BlockID.newBuilder().setHeight(height).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
// jni
|
||||||
|
val converter: JniConverter = JniConverter()
|
||||||
|
|
||||||
|
// db
|
||||||
|
private lateinit var dao: CompactBlockDao
|
||||||
|
private lateinit var db: CompactBlockDb
|
||||||
|
private const val cacheDbName = "dummy-cache-glue.db"
|
||||||
|
private const val cacheDbPath = "/data/user/0/cash.z.wallet.sdk.test/databases/$cacheDbName"
|
||||||
|
|
||||||
|
// grpc
|
||||||
|
lateinit var blockingStub: CompactTxStreamerGrpc.CompactTxStreamerBlockingStub
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
@JvmStatic
|
||||||
|
fun setup() {
|
||||||
|
converter.initLogs()
|
||||||
|
|
||||||
|
val channel = ManagedChannelBuilder.forAddress("10.0.2.2", 9067).usePlaintext().build()
|
||||||
|
blockingStub = CompactTxStreamerGrpc.newBlockingStub(channel)
|
||||||
|
|
||||||
|
db = Room
|
||||||
|
.databaseBuilder(
|
||||||
|
ApplicationProvider.getApplicationContext(),
|
||||||
|
CompactBlockDb::class.java,
|
||||||
|
cacheDbName
|
||||||
|
)
|
||||||
|
.setJournalMode(RoomDatabase.JournalMode.TRUNCATE)
|
||||||
|
.fallbackToDestructiveMigration()
|
||||||
|
.build()
|
||||||
|
.apply { dao = complactBlockDao() }
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
@JvmStatic
|
||||||
|
fun close() {
|
||||||
|
db.close()
|
||||||
|
(blockingStub.channel as ManagedChannel).shutdown().awaitTermination(2000L, TimeUnit.MILLISECONDS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,532 @@
|
||||||
|
package rpc;
|
||||||
|
|
||||||
|
import static io.grpc.MethodDescriptor.generateFullMethodName;
|
||||||
|
import static io.grpc.stub.ClientCalls.asyncBidiStreamingCall;
|
||||||
|
import static io.grpc.stub.ClientCalls.asyncClientStreamingCall;
|
||||||
|
import static io.grpc.stub.ClientCalls.asyncServerStreamingCall;
|
||||||
|
import static io.grpc.stub.ClientCalls.asyncUnaryCall;
|
||||||
|
import static io.grpc.stub.ClientCalls.blockingServerStreamingCall;
|
||||||
|
import static io.grpc.stub.ClientCalls.blockingUnaryCall;
|
||||||
|
import static io.grpc.stub.ClientCalls.futureUnaryCall;
|
||||||
|
import static io.grpc.stub.ServerCalls.asyncBidiStreamingCall;
|
||||||
|
import static io.grpc.stub.ServerCalls.asyncClientStreamingCall;
|
||||||
|
import static io.grpc.stub.ServerCalls.asyncServerStreamingCall;
|
||||||
|
import static io.grpc.stub.ServerCalls.asyncUnaryCall;
|
||||||
|
import static io.grpc.stub.ServerCalls.asyncUnimplementedStreamingCall;
|
||||||
|
import static io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
@javax.annotation.Generated(
|
||||||
|
value = "by gRPC proto compiler (version 1.17.1)",
|
||||||
|
comments = "Source: service.proto")
|
||||||
|
public final class CompactTxStreamerGrpc {
|
||||||
|
|
||||||
|
private CompactTxStreamerGrpc() {}
|
||||||
|
|
||||||
|
public static final String SERVICE_NAME = "rpc.CompactTxStreamer";
|
||||||
|
|
||||||
|
// Static method descriptors that strictly reflect the proto.
|
||||||
|
private static volatile io.grpc.MethodDescriptor<rpc.Service.ChainSpec,
|
||||||
|
rpc.Service.BlockID> getGetLatestBlockMethod;
|
||||||
|
|
||||||
|
@io.grpc.stub.annotations.RpcMethod(
|
||||||
|
fullMethodName = SERVICE_NAME + '/' + "GetLatestBlock",
|
||||||
|
requestType = rpc.Service.ChainSpec.class,
|
||||||
|
responseType = rpc.Service.BlockID.class,
|
||||||
|
methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
|
||||||
|
public static io.grpc.MethodDescriptor<rpc.Service.ChainSpec,
|
||||||
|
rpc.Service.BlockID> getGetLatestBlockMethod() {
|
||||||
|
io.grpc.MethodDescriptor<rpc.Service.ChainSpec, rpc.Service.BlockID> getGetLatestBlockMethod;
|
||||||
|
if ((getGetLatestBlockMethod = CompactTxStreamerGrpc.getGetLatestBlockMethod) == null) {
|
||||||
|
synchronized (CompactTxStreamerGrpc.class) {
|
||||||
|
if ((getGetLatestBlockMethod = CompactTxStreamerGrpc.getGetLatestBlockMethod) == null) {
|
||||||
|
CompactTxStreamerGrpc.getGetLatestBlockMethod = getGetLatestBlockMethod =
|
||||||
|
io.grpc.MethodDescriptor.<rpc.Service.ChainSpec, rpc.Service.BlockID>newBuilder()
|
||||||
|
.setType(io.grpc.MethodDescriptor.MethodType.UNARY)
|
||||||
|
.setFullMethodName(generateFullMethodName(
|
||||||
|
"rpc.CompactTxStreamer", "GetLatestBlock"))
|
||||||
|
.setSampledToLocalTracing(true)
|
||||||
|
.setRequestMarshaller(io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(
|
||||||
|
rpc.Service.ChainSpec.getDefaultInstance()))
|
||||||
|
.setResponseMarshaller(io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(
|
||||||
|
rpc.Service.BlockID.getDefaultInstance()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getGetLatestBlockMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static volatile io.grpc.MethodDescriptor<rpc.Service.BlockID,
|
||||||
|
rpc.CompactFormats.CompactBlock> getGetBlockMethod;
|
||||||
|
|
||||||
|
@io.grpc.stub.annotations.RpcMethod(
|
||||||
|
fullMethodName = SERVICE_NAME + '/' + "GetBlock",
|
||||||
|
requestType = rpc.Service.BlockID.class,
|
||||||
|
responseType = rpc.CompactFormats.CompactBlock.class,
|
||||||
|
methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
|
||||||
|
public static io.grpc.MethodDescriptor<rpc.Service.BlockID,
|
||||||
|
rpc.CompactFormats.CompactBlock> getGetBlockMethod() {
|
||||||
|
io.grpc.MethodDescriptor<rpc.Service.BlockID, rpc.CompactFormats.CompactBlock> getGetBlockMethod;
|
||||||
|
if ((getGetBlockMethod = CompactTxStreamerGrpc.getGetBlockMethod) == null) {
|
||||||
|
synchronized (CompactTxStreamerGrpc.class) {
|
||||||
|
if ((getGetBlockMethod = CompactTxStreamerGrpc.getGetBlockMethod) == null) {
|
||||||
|
CompactTxStreamerGrpc.getGetBlockMethod = getGetBlockMethod =
|
||||||
|
io.grpc.MethodDescriptor.<rpc.Service.BlockID, rpc.CompactFormats.CompactBlock>newBuilder()
|
||||||
|
.setType(io.grpc.MethodDescriptor.MethodType.UNARY)
|
||||||
|
.setFullMethodName(generateFullMethodName(
|
||||||
|
"rpc.CompactTxStreamer", "GetBlock"))
|
||||||
|
.setSampledToLocalTracing(true)
|
||||||
|
.setRequestMarshaller(io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(
|
||||||
|
rpc.Service.BlockID.getDefaultInstance()))
|
||||||
|
.setResponseMarshaller(io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(
|
||||||
|
rpc.CompactFormats.CompactBlock.getDefaultInstance()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getGetBlockMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static volatile io.grpc.MethodDescriptor<rpc.Service.BlockRange,
|
||||||
|
rpc.CompactFormats.CompactBlock> getGetBlockRangeMethod;
|
||||||
|
|
||||||
|
@io.grpc.stub.annotations.RpcMethod(
|
||||||
|
fullMethodName = SERVICE_NAME + '/' + "GetBlockRange",
|
||||||
|
requestType = rpc.Service.BlockRange.class,
|
||||||
|
responseType = rpc.CompactFormats.CompactBlock.class,
|
||||||
|
methodType = io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING)
|
||||||
|
public static io.grpc.MethodDescriptor<rpc.Service.BlockRange,
|
||||||
|
rpc.CompactFormats.CompactBlock> getGetBlockRangeMethod() {
|
||||||
|
io.grpc.MethodDescriptor<rpc.Service.BlockRange, rpc.CompactFormats.CompactBlock> getGetBlockRangeMethod;
|
||||||
|
if ((getGetBlockRangeMethod = CompactTxStreamerGrpc.getGetBlockRangeMethod) == null) {
|
||||||
|
synchronized (CompactTxStreamerGrpc.class) {
|
||||||
|
if ((getGetBlockRangeMethod = CompactTxStreamerGrpc.getGetBlockRangeMethod) == null) {
|
||||||
|
CompactTxStreamerGrpc.getGetBlockRangeMethod = getGetBlockRangeMethod =
|
||||||
|
io.grpc.MethodDescriptor.<rpc.Service.BlockRange, rpc.CompactFormats.CompactBlock>newBuilder()
|
||||||
|
.setType(io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING)
|
||||||
|
.setFullMethodName(generateFullMethodName(
|
||||||
|
"rpc.CompactTxStreamer", "GetBlockRange"))
|
||||||
|
.setSampledToLocalTracing(true)
|
||||||
|
.setRequestMarshaller(io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(
|
||||||
|
rpc.Service.BlockRange.getDefaultInstance()))
|
||||||
|
.setResponseMarshaller(io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(
|
||||||
|
rpc.CompactFormats.CompactBlock.getDefaultInstance()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getGetBlockRangeMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static volatile io.grpc.MethodDescriptor<rpc.Service.TxFilter,
|
||||||
|
rpc.Service.RawTransaction> getGetTransactionMethod;
|
||||||
|
|
||||||
|
@io.grpc.stub.annotations.RpcMethod(
|
||||||
|
fullMethodName = SERVICE_NAME + '/' + "GetTransaction",
|
||||||
|
requestType = rpc.Service.TxFilter.class,
|
||||||
|
responseType = rpc.Service.RawTransaction.class,
|
||||||
|
methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
|
||||||
|
public static io.grpc.MethodDescriptor<rpc.Service.TxFilter,
|
||||||
|
rpc.Service.RawTransaction> getGetTransactionMethod() {
|
||||||
|
io.grpc.MethodDescriptor<rpc.Service.TxFilter, rpc.Service.RawTransaction> getGetTransactionMethod;
|
||||||
|
if ((getGetTransactionMethod = CompactTxStreamerGrpc.getGetTransactionMethod) == null) {
|
||||||
|
synchronized (CompactTxStreamerGrpc.class) {
|
||||||
|
if ((getGetTransactionMethod = CompactTxStreamerGrpc.getGetTransactionMethod) == null) {
|
||||||
|
CompactTxStreamerGrpc.getGetTransactionMethod = getGetTransactionMethod =
|
||||||
|
io.grpc.MethodDescriptor.<rpc.Service.TxFilter, rpc.Service.RawTransaction>newBuilder()
|
||||||
|
.setType(io.grpc.MethodDescriptor.MethodType.UNARY)
|
||||||
|
.setFullMethodName(generateFullMethodName(
|
||||||
|
"rpc.CompactTxStreamer", "GetTransaction"))
|
||||||
|
.setSampledToLocalTracing(true)
|
||||||
|
.setRequestMarshaller(io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(
|
||||||
|
rpc.Service.TxFilter.getDefaultInstance()))
|
||||||
|
.setResponseMarshaller(io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(
|
||||||
|
rpc.Service.RawTransaction.getDefaultInstance()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getGetTransactionMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static volatile io.grpc.MethodDescriptor<rpc.Service.RawTransaction,
|
||||||
|
rpc.Service.SendResponse> getSendTransactionMethod;
|
||||||
|
|
||||||
|
@io.grpc.stub.annotations.RpcMethod(
|
||||||
|
fullMethodName = SERVICE_NAME + '/' + "SendTransaction",
|
||||||
|
requestType = rpc.Service.RawTransaction.class,
|
||||||
|
responseType = rpc.Service.SendResponse.class,
|
||||||
|
methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
|
||||||
|
public static io.grpc.MethodDescriptor<rpc.Service.RawTransaction,
|
||||||
|
rpc.Service.SendResponse> getSendTransactionMethod() {
|
||||||
|
io.grpc.MethodDescriptor<rpc.Service.RawTransaction, rpc.Service.SendResponse> getSendTransactionMethod;
|
||||||
|
if ((getSendTransactionMethod = CompactTxStreamerGrpc.getSendTransactionMethod) == null) {
|
||||||
|
synchronized (CompactTxStreamerGrpc.class) {
|
||||||
|
if ((getSendTransactionMethod = CompactTxStreamerGrpc.getSendTransactionMethod) == null) {
|
||||||
|
CompactTxStreamerGrpc.getSendTransactionMethod = getSendTransactionMethod =
|
||||||
|
io.grpc.MethodDescriptor.<rpc.Service.RawTransaction, rpc.Service.SendResponse>newBuilder()
|
||||||
|
.setType(io.grpc.MethodDescriptor.MethodType.UNARY)
|
||||||
|
.setFullMethodName(generateFullMethodName(
|
||||||
|
"rpc.CompactTxStreamer", "SendTransaction"))
|
||||||
|
.setSampledToLocalTracing(true)
|
||||||
|
.setRequestMarshaller(io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(
|
||||||
|
rpc.Service.RawTransaction.getDefaultInstance()))
|
||||||
|
.setResponseMarshaller(io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(
|
||||||
|
rpc.Service.SendResponse.getDefaultInstance()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getSendTransactionMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new async stub that supports all call types for the service
|
||||||
|
*/
|
||||||
|
public static CompactTxStreamerStub newStub(io.grpc.Channel channel) {
|
||||||
|
return new CompactTxStreamerStub(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new blocking-style stub that supports unary and streaming output calls on the service
|
||||||
|
*/
|
||||||
|
public static CompactTxStreamerBlockingStub newBlockingStub(
|
||||||
|
io.grpc.Channel channel) {
|
||||||
|
return new CompactTxStreamerBlockingStub(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new ListenableFuture-style stub that supports unary calls on the service
|
||||||
|
*/
|
||||||
|
public static CompactTxStreamerFutureStub newFutureStub(
|
||||||
|
io.grpc.Channel channel) {
|
||||||
|
return new CompactTxStreamerFutureStub(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public static abstract class CompactTxStreamerImplBase implements io.grpc.BindableService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public void getLatestBlock(rpc.Service.ChainSpec request,
|
||||||
|
io.grpc.stub.StreamObserver<rpc.Service.BlockID> responseObserver) {
|
||||||
|
asyncUnimplementedUnaryCall(getGetLatestBlockMethod(), responseObserver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public void getBlock(rpc.Service.BlockID request,
|
||||||
|
io.grpc.stub.StreamObserver<rpc.CompactFormats.CompactBlock> responseObserver) {
|
||||||
|
asyncUnimplementedUnaryCall(getGetBlockMethod(), responseObserver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public void getBlockRange(rpc.Service.BlockRange request,
|
||||||
|
io.grpc.stub.StreamObserver<rpc.CompactFormats.CompactBlock> responseObserver) {
|
||||||
|
asyncUnimplementedUnaryCall(getGetBlockRangeMethod(), responseObserver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public void getTransaction(rpc.Service.TxFilter request,
|
||||||
|
io.grpc.stub.StreamObserver<rpc.Service.RawTransaction> responseObserver) {
|
||||||
|
asyncUnimplementedUnaryCall(getGetTransactionMethod(), responseObserver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public void sendTransaction(rpc.Service.RawTransaction request,
|
||||||
|
io.grpc.stub.StreamObserver<rpc.Service.SendResponse> responseObserver) {
|
||||||
|
asyncUnimplementedUnaryCall(getSendTransactionMethod(), responseObserver);
|
||||||
|
}
|
||||||
|
|
||||||
|
@java.lang.Override public final io.grpc.ServerServiceDefinition bindService() {
|
||||||
|
return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor())
|
||||||
|
.addMethod(
|
||||||
|
getGetLatestBlockMethod(),
|
||||||
|
asyncUnaryCall(
|
||||||
|
new MethodHandlers<
|
||||||
|
rpc.Service.ChainSpec,
|
||||||
|
rpc.Service.BlockID>(
|
||||||
|
this, METHODID_GET_LATEST_BLOCK)))
|
||||||
|
.addMethod(
|
||||||
|
getGetBlockMethod(),
|
||||||
|
asyncUnaryCall(
|
||||||
|
new MethodHandlers<
|
||||||
|
rpc.Service.BlockID,
|
||||||
|
rpc.CompactFormats.CompactBlock>(
|
||||||
|
this, METHODID_GET_BLOCK)))
|
||||||
|
.addMethod(
|
||||||
|
getGetBlockRangeMethod(),
|
||||||
|
asyncServerStreamingCall(
|
||||||
|
new MethodHandlers<
|
||||||
|
rpc.Service.BlockRange,
|
||||||
|
rpc.CompactFormats.CompactBlock>(
|
||||||
|
this, METHODID_GET_BLOCK_RANGE)))
|
||||||
|
.addMethod(
|
||||||
|
getGetTransactionMethod(),
|
||||||
|
asyncUnaryCall(
|
||||||
|
new MethodHandlers<
|
||||||
|
rpc.Service.TxFilter,
|
||||||
|
rpc.Service.RawTransaction>(
|
||||||
|
this, METHODID_GET_TRANSACTION)))
|
||||||
|
.addMethod(
|
||||||
|
getSendTransactionMethod(),
|
||||||
|
asyncUnaryCall(
|
||||||
|
new MethodHandlers<
|
||||||
|
rpc.Service.RawTransaction,
|
||||||
|
rpc.Service.SendResponse>(
|
||||||
|
this, METHODID_SEND_TRANSACTION)))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public static final class CompactTxStreamerStub extends io.grpc.stub.AbstractStub<CompactTxStreamerStub> {
|
||||||
|
private CompactTxStreamerStub(io.grpc.Channel channel) {
|
||||||
|
super(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CompactTxStreamerStub(io.grpc.Channel channel,
|
||||||
|
io.grpc.CallOptions callOptions) {
|
||||||
|
super(channel, callOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@java.lang.Override
|
||||||
|
protected CompactTxStreamerStub build(io.grpc.Channel channel,
|
||||||
|
io.grpc.CallOptions callOptions) {
|
||||||
|
return new CompactTxStreamerStub(channel, callOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public void getLatestBlock(rpc.Service.ChainSpec request,
|
||||||
|
io.grpc.stub.StreamObserver<rpc.Service.BlockID> responseObserver) {
|
||||||
|
asyncUnaryCall(
|
||||||
|
getChannel().newCall(getGetLatestBlockMethod(), getCallOptions()), request, responseObserver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public void getBlock(rpc.Service.BlockID request,
|
||||||
|
io.grpc.stub.StreamObserver<rpc.CompactFormats.CompactBlock> responseObserver) {
|
||||||
|
asyncUnaryCall(
|
||||||
|
getChannel().newCall(getGetBlockMethod(), getCallOptions()), request, responseObserver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public void getBlockRange(rpc.Service.BlockRange request,
|
||||||
|
io.grpc.stub.StreamObserver<rpc.CompactFormats.CompactBlock> responseObserver) {
|
||||||
|
asyncServerStreamingCall(
|
||||||
|
getChannel().newCall(getGetBlockRangeMethod(), getCallOptions()), request, responseObserver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public void getTransaction(rpc.Service.TxFilter request,
|
||||||
|
io.grpc.stub.StreamObserver<rpc.Service.RawTransaction> responseObserver) {
|
||||||
|
asyncUnaryCall(
|
||||||
|
getChannel().newCall(getGetTransactionMethod(), getCallOptions()), request, responseObserver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public void sendTransaction(rpc.Service.RawTransaction request,
|
||||||
|
io.grpc.stub.StreamObserver<rpc.Service.SendResponse> responseObserver) {
|
||||||
|
asyncUnaryCall(
|
||||||
|
getChannel().newCall(getSendTransactionMethod(), getCallOptions()), request, responseObserver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public static final class CompactTxStreamerBlockingStub extends io.grpc.stub.AbstractStub<CompactTxStreamerBlockingStub> {
|
||||||
|
private CompactTxStreamerBlockingStub(io.grpc.Channel channel) {
|
||||||
|
super(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CompactTxStreamerBlockingStub(io.grpc.Channel channel,
|
||||||
|
io.grpc.CallOptions callOptions) {
|
||||||
|
super(channel, callOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@java.lang.Override
|
||||||
|
protected CompactTxStreamerBlockingStub build(io.grpc.Channel channel,
|
||||||
|
io.grpc.CallOptions callOptions) {
|
||||||
|
return new CompactTxStreamerBlockingStub(channel, callOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public rpc.Service.BlockID getLatestBlock(rpc.Service.ChainSpec request) {
|
||||||
|
return blockingUnaryCall(
|
||||||
|
getChannel(), getGetLatestBlockMethod(), getCallOptions(), request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public rpc.CompactFormats.CompactBlock getBlock(rpc.Service.BlockID request) {
|
||||||
|
return blockingUnaryCall(
|
||||||
|
getChannel(), getGetBlockMethod(), getCallOptions(), request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public java.util.Iterator<rpc.CompactFormats.CompactBlock> getBlockRange(
|
||||||
|
rpc.Service.BlockRange request) {
|
||||||
|
return blockingServerStreamingCall(
|
||||||
|
getChannel(), getGetBlockRangeMethod(), getCallOptions(), request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public rpc.Service.RawTransaction getTransaction(rpc.Service.TxFilter request) {
|
||||||
|
return blockingUnaryCall(
|
||||||
|
getChannel(), getGetTransactionMethod(), getCallOptions(), request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public rpc.Service.SendResponse sendTransaction(rpc.Service.RawTransaction request) {
|
||||||
|
return blockingUnaryCall(
|
||||||
|
getChannel(), getSendTransactionMethod(), getCallOptions(), request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public static final class CompactTxStreamerFutureStub extends io.grpc.stub.AbstractStub<CompactTxStreamerFutureStub> {
|
||||||
|
private CompactTxStreamerFutureStub(io.grpc.Channel channel) {
|
||||||
|
super(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CompactTxStreamerFutureStub(io.grpc.Channel channel,
|
||||||
|
io.grpc.CallOptions callOptions) {
|
||||||
|
super(channel, callOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@java.lang.Override
|
||||||
|
protected CompactTxStreamerFutureStub build(io.grpc.Channel channel,
|
||||||
|
io.grpc.CallOptions callOptions) {
|
||||||
|
return new CompactTxStreamerFutureStub(channel, callOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public com.google.common.util.concurrent.ListenableFuture<rpc.Service.BlockID> getLatestBlock(
|
||||||
|
rpc.Service.ChainSpec request) {
|
||||||
|
return futureUnaryCall(
|
||||||
|
getChannel().newCall(getGetLatestBlockMethod(), getCallOptions()), request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public com.google.common.util.concurrent.ListenableFuture<rpc.CompactFormats.CompactBlock> getBlock(
|
||||||
|
rpc.Service.BlockID request) {
|
||||||
|
return futureUnaryCall(
|
||||||
|
getChannel().newCall(getGetBlockMethod(), getCallOptions()), request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public com.google.common.util.concurrent.ListenableFuture<rpc.Service.RawTransaction> getTransaction(
|
||||||
|
rpc.Service.TxFilter request) {
|
||||||
|
return futureUnaryCall(
|
||||||
|
getChannel().newCall(getGetTransactionMethod(), getCallOptions()), request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public com.google.common.util.concurrent.ListenableFuture<rpc.Service.SendResponse> sendTransaction(
|
||||||
|
rpc.Service.RawTransaction request) {
|
||||||
|
return futureUnaryCall(
|
||||||
|
getChannel().newCall(getSendTransactionMethod(), getCallOptions()), request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int METHODID_GET_LATEST_BLOCK = 0;
|
||||||
|
private static final int METHODID_GET_BLOCK = 1;
|
||||||
|
private static final int METHODID_GET_BLOCK_RANGE = 2;
|
||||||
|
private static final int METHODID_GET_TRANSACTION = 3;
|
||||||
|
private static final int METHODID_SEND_TRANSACTION = 4;
|
||||||
|
|
||||||
|
private static final class MethodHandlers<Req, Resp> implements
|
||||||
|
io.grpc.stub.ServerCalls.UnaryMethod<Req, Resp>,
|
||||||
|
io.grpc.stub.ServerCalls.ServerStreamingMethod<Req, Resp>,
|
||||||
|
io.grpc.stub.ServerCalls.ClientStreamingMethod<Req, Resp>,
|
||||||
|
io.grpc.stub.ServerCalls.BidiStreamingMethod<Req, Resp> {
|
||||||
|
private final CompactTxStreamerImplBase serviceImpl;
|
||||||
|
private final int methodId;
|
||||||
|
|
||||||
|
MethodHandlers(CompactTxStreamerImplBase serviceImpl, int methodId) {
|
||||||
|
this.serviceImpl = serviceImpl;
|
||||||
|
this.methodId = methodId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@java.lang.Override
|
||||||
|
@java.lang.SuppressWarnings("unchecked")
|
||||||
|
public void invoke(Req request, io.grpc.stub.StreamObserver<Resp> responseObserver) {
|
||||||
|
switch (methodId) {
|
||||||
|
case METHODID_GET_LATEST_BLOCK:
|
||||||
|
serviceImpl.getLatestBlock((rpc.Service.ChainSpec) request,
|
||||||
|
(io.grpc.stub.StreamObserver<rpc.Service.BlockID>) responseObserver);
|
||||||
|
break;
|
||||||
|
case METHODID_GET_BLOCK:
|
||||||
|
serviceImpl.getBlock((rpc.Service.BlockID) request,
|
||||||
|
(io.grpc.stub.StreamObserver<rpc.CompactFormats.CompactBlock>) responseObserver);
|
||||||
|
break;
|
||||||
|
case METHODID_GET_BLOCK_RANGE:
|
||||||
|
serviceImpl.getBlockRange((rpc.Service.BlockRange) request,
|
||||||
|
(io.grpc.stub.StreamObserver<rpc.CompactFormats.CompactBlock>) responseObserver);
|
||||||
|
break;
|
||||||
|
case METHODID_GET_TRANSACTION:
|
||||||
|
serviceImpl.getTransaction((rpc.Service.TxFilter) request,
|
||||||
|
(io.grpc.stub.StreamObserver<rpc.Service.RawTransaction>) responseObserver);
|
||||||
|
break;
|
||||||
|
case METHODID_SEND_TRANSACTION:
|
||||||
|
serviceImpl.sendTransaction((rpc.Service.RawTransaction) request,
|
||||||
|
(io.grpc.stub.StreamObserver<rpc.Service.SendResponse>) responseObserver);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@java.lang.Override
|
||||||
|
@java.lang.SuppressWarnings("unchecked")
|
||||||
|
public io.grpc.stub.StreamObserver<Req> invoke(
|
||||||
|
io.grpc.stub.StreamObserver<Resp> responseObserver) {
|
||||||
|
switch (methodId) {
|
||||||
|
default:
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static volatile io.grpc.ServiceDescriptor serviceDescriptor;
|
||||||
|
|
||||||
|
public static io.grpc.ServiceDescriptor getServiceDescriptor() {
|
||||||
|
io.grpc.ServiceDescriptor result = serviceDescriptor;
|
||||||
|
if (result == null) {
|
||||||
|
synchronized (CompactTxStreamerGrpc.class) {
|
||||||
|
result = serviceDescriptor;
|
||||||
|
if (result == null) {
|
||||||
|
serviceDescriptor = result = io.grpc.ServiceDescriptor.newBuilder(SERVICE_NAME)
|
||||||
|
.addMethod(getGetLatestBlockMethod())
|
||||||
|
.addMethod(getGetBlockMethod())
|
||||||
|
.addMethod(getGetBlockRangeMethod())
|
||||||
|
.addMethod(getGetTransactionMethod())
|
||||||
|
.addMethod(getSendTransactionMethod())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,505 @@
|
||||||
|
// Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||||
|
// source: WalletData.proto
|
||||||
|
|
||||||
|
package rpc;
|
||||||
|
|
||||||
|
public final class WalletDataOuterClass {
|
||||||
|
private WalletDataOuterClass() {}
|
||||||
|
public static void registerAllExtensions(
|
||||||
|
com.google.protobuf.ExtensionRegistryLite registry) {
|
||||||
|
}
|
||||||
|
public interface WalletDataOrBuilder extends
|
||||||
|
// @@protoc_insertion_point(interface_extends:rpc.WalletData)
|
||||||
|
com.google.protobuf.MessageLiteOrBuilder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional string name = 1;</code>
|
||||||
|
*/
|
||||||
|
java.lang.String getName();
|
||||||
|
/**
|
||||||
|
* <code>optional string name = 1;</code>
|
||||||
|
*/
|
||||||
|
com.google.protobuf.ByteString
|
||||||
|
getNameBytes();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional int32 id = 2;</code>
|
||||||
|
*/
|
||||||
|
int getId();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional string emails = 3;</code>
|
||||||
|
*/
|
||||||
|
java.lang.String getEmails();
|
||||||
|
/**
|
||||||
|
* <code>optional string emails = 3;</code>
|
||||||
|
*/
|
||||||
|
com.google.protobuf.ByteString
|
||||||
|
getEmailsBytes();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Protobuf type {@code rpc.WalletData}
|
||||||
|
*/
|
||||||
|
public static final class WalletData extends
|
||||||
|
com.google.protobuf.GeneratedMessageLite<
|
||||||
|
WalletData, WalletData.Builder> implements
|
||||||
|
// @@protoc_insertion_point(message_implements:rpc.WalletData)
|
||||||
|
WalletDataOrBuilder {
|
||||||
|
private WalletData() {
|
||||||
|
name_ = "";
|
||||||
|
emails_ = "";
|
||||||
|
}
|
||||||
|
public static final int NAME_FIELD_NUMBER = 1;
|
||||||
|
private java.lang.String name_;
|
||||||
|
/**
|
||||||
|
* <code>optional string name = 1;</code>
|
||||||
|
*/
|
||||||
|
public java.lang.String getName() {
|
||||||
|
return name_;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* <code>optional string name = 1;</code>
|
||||||
|
*/
|
||||||
|
public com.google.protobuf.ByteString
|
||||||
|
getNameBytes() {
|
||||||
|
return com.google.protobuf.ByteString.copyFromUtf8(name_);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* <code>optional string name = 1;</code>
|
||||||
|
*/
|
||||||
|
private void setName(
|
||||||
|
java.lang.String value) {
|
||||||
|
if (value == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
|
||||||
|
name_ = value;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* <code>optional string name = 1;</code>
|
||||||
|
*/
|
||||||
|
private void clearName() {
|
||||||
|
|
||||||
|
name_ = getDefaultInstance().getName();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* <code>optional string name = 1;</code>
|
||||||
|
*/
|
||||||
|
private void setNameBytes(
|
||||||
|
com.google.protobuf.ByteString value) {
|
||||||
|
if (value == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
checkByteStringIsUtf8(value);
|
||||||
|
|
||||||
|
name_ = value.toStringUtf8();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final int ID_FIELD_NUMBER = 2;
|
||||||
|
private int id_;
|
||||||
|
/**
|
||||||
|
* <code>optional int32 id = 2;</code>
|
||||||
|
*/
|
||||||
|
public int getId() {
|
||||||
|
return id_;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* <code>optional int32 id = 2;</code>
|
||||||
|
*/
|
||||||
|
private void setId(int value) {
|
||||||
|
|
||||||
|
id_ = value;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* <code>optional int32 id = 2;</code>
|
||||||
|
*/
|
||||||
|
private void clearId() {
|
||||||
|
|
||||||
|
id_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final int EMAILS_FIELD_NUMBER = 3;
|
||||||
|
private java.lang.String emails_;
|
||||||
|
/**
|
||||||
|
* <code>optional string emails = 3;</code>
|
||||||
|
*/
|
||||||
|
public java.lang.String getEmails() {
|
||||||
|
return emails_;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* <code>optional string emails = 3;</code>
|
||||||
|
*/
|
||||||
|
public com.google.protobuf.ByteString
|
||||||
|
getEmailsBytes() {
|
||||||
|
return com.google.protobuf.ByteString.copyFromUtf8(emails_);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* <code>optional string emails = 3;</code>
|
||||||
|
*/
|
||||||
|
private void setEmails(
|
||||||
|
java.lang.String value) {
|
||||||
|
if (value == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
|
||||||
|
emails_ = value;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* <code>optional string emails = 3;</code>
|
||||||
|
*/
|
||||||
|
private void clearEmails() {
|
||||||
|
|
||||||
|
emails_ = getDefaultInstance().getEmails();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* <code>optional string emails = 3;</code>
|
||||||
|
*/
|
||||||
|
private void setEmailsBytes(
|
||||||
|
com.google.protobuf.ByteString value) {
|
||||||
|
if (value == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
checkByteStringIsUtf8(value);
|
||||||
|
|
||||||
|
emails_ = value.toStringUtf8();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeTo(com.google.protobuf.CodedOutputStream output)
|
||||||
|
throws java.io.IOException {
|
||||||
|
if (!name_.isEmpty()) {
|
||||||
|
output.writeString(1, getName());
|
||||||
|
}
|
||||||
|
if (id_ != 0) {
|
||||||
|
output.writeInt32(2, id_);
|
||||||
|
}
|
||||||
|
if (!emails_.isEmpty()) {
|
||||||
|
output.writeString(3, getEmails());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSerializedSize() {
|
||||||
|
int size = memoizedSerializedSize;
|
||||||
|
if (size != -1) return size;
|
||||||
|
|
||||||
|
size = 0;
|
||||||
|
if (!name_.isEmpty()) {
|
||||||
|
size += com.google.protobuf.CodedOutputStream
|
||||||
|
.computeStringSize(1, getName());
|
||||||
|
}
|
||||||
|
if (id_ != 0) {
|
||||||
|
size += com.google.protobuf.CodedOutputStream
|
||||||
|
.computeInt32Size(2, id_);
|
||||||
|
}
|
||||||
|
if (!emails_.isEmpty()) {
|
||||||
|
size += com.google.protobuf.CodedOutputStream
|
||||||
|
.computeStringSize(3, getEmails());
|
||||||
|
}
|
||||||
|
memoizedSerializedSize = size;
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static rpc.WalletDataOuterClass.WalletData parseFrom(
|
||||||
|
com.google.protobuf.ByteString data)
|
||||||
|
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||||
|
return com.google.protobuf.GeneratedMessageLite.parseFrom(
|
||||||
|
DEFAULT_INSTANCE, data);
|
||||||
|
}
|
||||||
|
public static rpc.WalletDataOuterClass.WalletData parseFrom(
|
||||||
|
com.google.protobuf.ByteString data,
|
||||||
|
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||||
|
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||||
|
return com.google.protobuf.GeneratedMessageLite.parseFrom(
|
||||||
|
DEFAULT_INSTANCE, data, extensionRegistry);
|
||||||
|
}
|
||||||
|
public static rpc.WalletDataOuterClass.WalletData parseFrom(byte[] data)
|
||||||
|
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||||
|
return com.google.protobuf.GeneratedMessageLite.parseFrom(
|
||||||
|
DEFAULT_INSTANCE, data);
|
||||||
|
}
|
||||||
|
public static rpc.WalletDataOuterClass.WalletData parseFrom(
|
||||||
|
byte[] data,
|
||||||
|
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||||
|
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||||
|
return com.google.protobuf.GeneratedMessageLite.parseFrom(
|
||||||
|
DEFAULT_INSTANCE, data, extensionRegistry);
|
||||||
|
}
|
||||||
|
public static rpc.WalletDataOuterClass.WalletData parseFrom(java.io.InputStream input)
|
||||||
|
throws java.io.IOException {
|
||||||
|
return com.google.protobuf.GeneratedMessageLite.parseFrom(
|
||||||
|
DEFAULT_INSTANCE, input);
|
||||||
|
}
|
||||||
|
public static rpc.WalletDataOuterClass.WalletData parseFrom(
|
||||||
|
java.io.InputStream input,
|
||||||
|
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||||
|
throws java.io.IOException {
|
||||||
|
return com.google.protobuf.GeneratedMessageLite.parseFrom(
|
||||||
|
DEFAULT_INSTANCE, input, extensionRegistry);
|
||||||
|
}
|
||||||
|
public static rpc.WalletDataOuterClass.WalletData parseDelimitedFrom(java.io.InputStream input)
|
||||||
|
throws java.io.IOException {
|
||||||
|
return parseDelimitedFrom(DEFAULT_INSTANCE, input);
|
||||||
|
}
|
||||||
|
public static rpc.WalletDataOuterClass.WalletData parseDelimitedFrom(
|
||||||
|
java.io.InputStream input,
|
||||||
|
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||||
|
throws java.io.IOException {
|
||||||
|
return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);
|
||||||
|
}
|
||||||
|
public static rpc.WalletDataOuterClass.WalletData parseFrom(
|
||||||
|
com.google.protobuf.CodedInputStream input)
|
||||||
|
throws java.io.IOException {
|
||||||
|
return com.google.protobuf.GeneratedMessageLite.parseFrom(
|
||||||
|
DEFAULT_INSTANCE, input);
|
||||||
|
}
|
||||||
|
public static rpc.WalletDataOuterClass.WalletData parseFrom(
|
||||||
|
com.google.protobuf.CodedInputStream input,
|
||||||
|
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||||
|
throws java.io.IOException {
|
||||||
|
return com.google.protobuf.GeneratedMessageLite.parseFrom(
|
||||||
|
DEFAULT_INSTANCE, input, extensionRegistry);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Builder newBuilder() {
|
||||||
|
return DEFAULT_INSTANCE.toBuilder();
|
||||||
|
}
|
||||||
|
public static Builder newBuilder(rpc.WalletDataOuterClass.WalletData prototype) {
|
||||||
|
return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protobuf type {@code rpc.WalletData}
|
||||||
|
*/
|
||||||
|
public static final class Builder extends
|
||||||
|
com.google.protobuf.GeneratedMessageLite.Builder<
|
||||||
|
rpc.WalletDataOuterClass.WalletData, Builder> implements
|
||||||
|
// @@protoc_insertion_point(builder_implements:rpc.WalletData)
|
||||||
|
rpc.WalletDataOuterClass.WalletDataOrBuilder {
|
||||||
|
// Construct using rpc.WalletDataOuterClass.WalletData.newBuilder()
|
||||||
|
private Builder() {
|
||||||
|
super(DEFAULT_INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional string name = 1;</code>
|
||||||
|
*/
|
||||||
|
public java.lang.String getName() {
|
||||||
|
return instance.getName();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* <code>optional string name = 1;</code>
|
||||||
|
*/
|
||||||
|
public com.google.protobuf.ByteString
|
||||||
|
getNameBytes() {
|
||||||
|
return instance.getNameBytes();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* <code>optional string name = 1;</code>
|
||||||
|
*/
|
||||||
|
public Builder setName(
|
||||||
|
java.lang.String value) {
|
||||||
|
copyOnWrite();
|
||||||
|
instance.setName(value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* <code>optional string name = 1;</code>
|
||||||
|
*/
|
||||||
|
public Builder clearName() {
|
||||||
|
copyOnWrite();
|
||||||
|
instance.clearName();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* <code>optional string name = 1;</code>
|
||||||
|
*/
|
||||||
|
public Builder setNameBytes(
|
||||||
|
com.google.protobuf.ByteString value) {
|
||||||
|
copyOnWrite();
|
||||||
|
instance.setNameBytes(value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional int32 id = 2;</code>
|
||||||
|
*/
|
||||||
|
public int getId() {
|
||||||
|
return instance.getId();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* <code>optional int32 id = 2;</code>
|
||||||
|
*/
|
||||||
|
public Builder setId(int value) {
|
||||||
|
copyOnWrite();
|
||||||
|
instance.setId(value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* <code>optional int32 id = 2;</code>
|
||||||
|
*/
|
||||||
|
public Builder clearId() {
|
||||||
|
copyOnWrite();
|
||||||
|
instance.clearId();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional string emails = 3;</code>
|
||||||
|
*/
|
||||||
|
public java.lang.String getEmails() {
|
||||||
|
return instance.getEmails();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* <code>optional string emails = 3;</code>
|
||||||
|
*/
|
||||||
|
public com.google.protobuf.ByteString
|
||||||
|
getEmailsBytes() {
|
||||||
|
return instance.getEmailsBytes();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* <code>optional string emails = 3;</code>
|
||||||
|
*/
|
||||||
|
public Builder setEmails(
|
||||||
|
java.lang.String value) {
|
||||||
|
copyOnWrite();
|
||||||
|
instance.setEmails(value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* <code>optional string emails = 3;</code>
|
||||||
|
*/
|
||||||
|
public Builder clearEmails() {
|
||||||
|
copyOnWrite();
|
||||||
|
instance.clearEmails();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* <code>optional string emails = 3;</code>
|
||||||
|
*/
|
||||||
|
public Builder setEmailsBytes(
|
||||||
|
com.google.protobuf.ByteString value) {
|
||||||
|
copyOnWrite();
|
||||||
|
instance.setEmailsBytes(value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @@protoc_insertion_point(builder_scope:rpc.WalletData)
|
||||||
|
}
|
||||||
|
protected final Object dynamicMethod(
|
||||||
|
com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,
|
||||||
|
Object arg0, Object arg1) {
|
||||||
|
switch (method) {
|
||||||
|
case NEW_MUTABLE_INSTANCE: {
|
||||||
|
return new rpc.WalletDataOuterClass.WalletData();
|
||||||
|
}
|
||||||
|
case IS_INITIALIZED: {
|
||||||
|
return DEFAULT_INSTANCE;
|
||||||
|
}
|
||||||
|
case MAKE_IMMUTABLE: {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
case NEW_BUILDER: {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
case VISIT: {
|
||||||
|
Visitor visitor = (Visitor) arg0;
|
||||||
|
rpc.WalletDataOuterClass.WalletData other = (rpc.WalletDataOuterClass.WalletData) arg1;
|
||||||
|
name_ = visitor.visitString(!name_.isEmpty(), name_,
|
||||||
|
!other.name_.isEmpty(), other.name_);
|
||||||
|
id_ = visitor.visitInt(id_ != 0, id_,
|
||||||
|
other.id_ != 0, other.id_);
|
||||||
|
emails_ = visitor.visitString(!emails_.isEmpty(), emails_,
|
||||||
|
!other.emails_.isEmpty(), other.emails_);
|
||||||
|
if (visitor == com.google.protobuf.GeneratedMessageLite.MergeFromVisitor
|
||||||
|
.INSTANCE) {
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
case MERGE_FROM_STREAM: {
|
||||||
|
com.google.protobuf.CodedInputStream input =
|
||||||
|
(com.google.protobuf.CodedInputStream) arg0;
|
||||||
|
com.google.protobuf.ExtensionRegistryLite extensionRegistry =
|
||||||
|
(com.google.protobuf.ExtensionRegistryLite) arg1;
|
||||||
|
try {
|
||||||
|
boolean done = false;
|
||||||
|
while (!done) {
|
||||||
|
int tag = input.readTag();
|
||||||
|
switch (tag) {
|
||||||
|
case 0:
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
default: {
|
||||||
|
if (!input.skipField(tag)) {
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 10: {
|
||||||
|
String s = input.readStringRequireUtf8();
|
||||||
|
|
||||||
|
name_ = s;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 16: {
|
||||||
|
|
||||||
|
id_ = input.readInt32();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 26: {
|
||||||
|
String s = input.readStringRequireUtf8();
|
||||||
|
|
||||||
|
emails_ = s;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
|
||||||
|
throw new RuntimeException(e.setUnfinishedMessage(this));
|
||||||
|
} catch (java.io.IOException e) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
new com.google.protobuf.InvalidProtocolBufferException(
|
||||||
|
e.getMessage()).setUnfinishedMessage(this));
|
||||||
|
} finally {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case GET_DEFAULT_INSTANCE: {
|
||||||
|
return DEFAULT_INSTANCE;
|
||||||
|
}
|
||||||
|
case GET_PARSER: {
|
||||||
|
if (PARSER == null) { synchronized (rpc.WalletDataOuterClass.WalletData.class) {
|
||||||
|
if (PARSER == null) {
|
||||||
|
PARSER = new DefaultInstanceBasedParser(DEFAULT_INSTANCE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return PARSER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// @@protoc_insertion_point(class_scope:rpc.WalletData)
|
||||||
|
private static final rpc.WalletDataOuterClass.WalletData DEFAULT_INSTANCE;
|
||||||
|
static {
|
||||||
|
DEFAULT_INSTANCE = new WalletData();
|
||||||
|
DEFAULT_INSTANCE.makeImmutable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static rpc.WalletDataOuterClass.WalletData getDefaultInstance() {
|
||||||
|
return DEFAULT_INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static volatile com.google.protobuf.Parser<WalletData> PARSER;
|
||||||
|
|
||||||
|
public static com.google.protobuf.Parser<WalletData> parser() {
|
||||||
|
return DEFAULT_INSTANCE.getParserForType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static {
|
||||||
|
}
|
||||||
|
|
||||||
|
// @@protoc_insertion_point(outer_class_scope)
|
||||||
|
}
|
|
@ -1,2 +1,6 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="cash.z.wallet.sdk"/>
|
package="cash.z.wallet.sdk">
|
||||||
|
<!--<application-->
|
||||||
|
<!--android:name="androidx.multidex.MultiDexApplication" />-->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
</manifest>
|
||||||
|
|
|
@ -5,7 +5,7 @@ import cash.z.wallet.sdk.vo.CompactBlock
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface CompactBlockDao {
|
interface CompactBlockDao {
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||||
fun insert(block: CompactBlock)
|
fun insert(block: CompactBlock)
|
||||||
|
|
||||||
@Query("SELECT * FROM compactblocks WHERE height = :height")
|
@Query("SELECT * FROM compactblocks WHERE height = :height")
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
package cash.z.wallet.sdk.data
|
||||||
|
|
||||||
|
import cash.z.wallet.sdk.ext.debug
|
||||||
|
import cash.z.wallet.sdk.ext.toBlockHeight
|
||||||
|
import io.grpc.ManagedChannelBuilder
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.coroutines.channels.BroadcastChannel
|
||||||
|
import kotlinx.coroutines.channels.ReceiveChannel
|
||||||
|
import rpc.CompactFormats.CompactBlock
|
||||||
|
import rpc.CompactTxStreamerGrpc
|
||||||
|
import rpc.Service
|
||||||
|
import java.io.Closeable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Downloads compact blocks to the database
|
||||||
|
*/
|
||||||
|
class CompactBlockDownloader(val scope: CoroutineScope) : CompactBlockSource {
|
||||||
|
|
||||||
|
private var connection: Connection? = null
|
||||||
|
|
||||||
|
override fun blocks(): ReceiveChannel<Result<CompactBlock>> = connection!!.subscribe()
|
||||||
|
|
||||||
|
fun start() {
|
||||||
|
connection = Connection()
|
||||||
|
scope.launch {
|
||||||
|
connection!!.loadBlockRange(373070L..373085L).join()
|
||||||
|
connection = Connection().open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stop() {
|
||||||
|
connection?.close()
|
||||||
|
connection = null
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class Connection: Closeable {
|
||||||
|
private var job: Job? = null
|
||||||
|
private var syncJob: Job? = null
|
||||||
|
private val compactBlockChannel = BroadcastChannel<Result<CompactBlock>>(100)
|
||||||
|
private val errorHandler: CoroutineExceptionHandler
|
||||||
|
|
||||||
|
val channel = ManagedChannelBuilder.forAddress("10.0.2.2", 9067).usePlaintext().build()
|
||||||
|
val blockingStub = CompactTxStreamerGrpc.newBlockingStub(channel)
|
||||||
|
|
||||||
|
init {
|
||||||
|
errorHandler = CoroutineExceptionHandler { _, error ->
|
||||||
|
debug("handling error: $error")
|
||||||
|
try {
|
||||||
|
debug("totally about to launch something sweet")
|
||||||
|
GlobalScope.launch {
|
||||||
|
debug("sending error")
|
||||||
|
compactBlockChannel.send(Result.failure(error))
|
||||||
|
debug("error sent")
|
||||||
|
}
|
||||||
|
|
||||||
|
}catch (t:Throwable) {
|
||||||
|
debug("failed to send error because of $t")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun subscribe() = compactBlockChannel.openSubscription()
|
||||||
|
|
||||||
|
fun loadBlockRange(range: LongRange) : Job {
|
||||||
|
syncJob = scope.launch {
|
||||||
|
if (isActive) {
|
||||||
|
debug("requesting a block range: ...")
|
||||||
|
channel.
|
||||||
|
val result = blockingStub.getBlockRange(
|
||||||
|
Service.BlockRange.newBuilder()
|
||||||
|
.setStart(range.first.toBlockHeight())
|
||||||
|
.setEnd(range.last.toBlockHeight())
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
while (result.hasNext()) {
|
||||||
|
try {
|
||||||
|
val nextBlock = result.next()
|
||||||
|
debug("received new block in range: ${nextBlock.height}")
|
||||||
|
|
||||||
|
async { debug("sending block from range: ${nextBlock.height}"); compactBlockChannel.send(Result.success(nextBlock)); debug("done sending block from range: ${nextBlock.height}") }
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
async { debug("sending failure"); compactBlockChannel.send(Result.failure(t)); debug("done sending failure"); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return syncJob!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun open(): Connection {
|
||||||
|
// TODO: use CoroutineScope.open to avoid the need to pass scope around
|
||||||
|
job = scope.launch {
|
||||||
|
var lastHeight = 0L
|
||||||
|
while (isActive) {
|
||||||
|
debug("requesting a block...")
|
||||||
|
val result = blockingStub.getLatestBlock(Service.ChainSpec.newBuilder().build())
|
||||||
|
if (result.height > lastHeight) {
|
||||||
|
debug("received new block: ${result.height}")
|
||||||
|
// if we have new data, send it and then wait a while
|
||||||
|
try {
|
||||||
|
|
||||||
|
async { debug("sending block: ${result.height}"); compactBlockChannel.send(Result.success(blockingStub.getBlock(result))); debug("done sending block: ${result.height}"); }
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
async { debug("sending failure"); compactBlockChannel.send(Result.failure(t)); debug("done sending failure"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
lastHeight = result.height
|
||||||
|
delay(25 * 1000)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
debug("received same old block: ${result.height}")
|
||||||
|
// otherwise keep checking fairly often until we have new data
|
||||||
|
delay(3000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
compactBlockChannel.cancel()
|
||||||
|
syncJob?.cancel()
|
||||||
|
syncJob = null
|
||||||
|
job?.cancel()
|
||||||
|
job = null
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package cash.z.wallet.sdk.data
|
||||||
|
|
||||||
|
import kotlinx.coroutines.channels.ReceiveChannel
|
||||||
|
import rpc.CompactFormats.CompactBlock
|
||||||
|
|
||||||
|
interface CompactBlockSource {
|
||||||
|
fun blocks(): ReceiveChannel<Result<CompactBlock>>
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
package cash.z.wallet.sdk.data
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.database.sqlite.SQLiteDatabase
|
||||||
|
import android.database.sqlite.SQLiteOpenHelper
|
||||||
|
|
||||||
|
class ScanResultDbCreator(context: Context) : SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION) {
|
||||||
|
override fun onCreate(db: SQLiteDatabase) {
|
||||||
|
SQL_CREATE_DB.split(";").forEach { db.execSQL(it.trim()) }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||||
|
onCreate(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDowngrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||||
|
onUpgrade(db, oldVersion, newVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val DB_NAME = "ScannedBlockResults.db"
|
||||||
|
const val DB_VERSION = 1
|
||||||
|
val SQL_CREATE_DB: String = """
|
||||||
|
CREATE TABLE IF NOT EXISTS blocks (
|
||||||
|
height INTEGER PRIMARY KEY,
|
||||||
|
time INTEGER,
|
||||||
|
sapling_tree BLOB
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS transactions (
|
||||||
|
id_tx INTEGER PRIMARY KEY,
|
||||||
|
txid BLOB NOT NULL UNIQUE,
|
||||||
|
block INTEGER,
|
||||||
|
raw BLOB,
|
||||||
|
FOREIGN KEY (block) REFERENCES blocks(height)
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS received_notes (
|
||||||
|
id_note INTEGER PRIMARY KEY,
|
||||||
|
tx INTEGER NOT NULL,
|
||||||
|
output_index INTEGER NOT NULL,
|
||||||
|
account INTEGER NOT NULL,
|
||||||
|
diversifier BLOB NOT NULL,
|
||||||
|
value INTEGER NOT NULL,
|
||||||
|
rcm BLOB NOT NULL,
|
||||||
|
nf BLOB NOT NULL UNIQUE,
|
||||||
|
memo BLOB,
|
||||||
|
spent INTEGER,
|
||||||
|
FOREIGN KEY (tx) REFERENCES transactions(id_tx),
|
||||||
|
FOREIGN KEY (spent) REFERENCES transactions(id_tx),
|
||||||
|
CONSTRAINT tx_output UNIQUE (tx, output_index)
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS sapling_witnesses (
|
||||||
|
id_witness INTEGER PRIMARY KEY,
|
||||||
|
note INTEGER NOT NULL,
|
||||||
|
block INTEGER NOT NULL,
|
||||||
|
witness BLOB NOT NULL,
|
||||||
|
FOREIGN KEY (note) REFERENCES received_notes(id_note),
|
||||||
|
FOREIGN KEY (block) REFERENCES blocks(height),
|
||||||
|
CONSTRAINT witness_height UNIQUE (note, block)
|
||||||
|
)
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
fun create(context: Context) {
|
||||||
|
val db = ScanResultDbCreator(context).writableDatabase
|
||||||
|
db.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
package cash.z.wallet.sdk.data
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.room.Room
|
||||||
|
import androidx.room.RoomDatabase
|
||||||
|
import cash.z.wallet.sdk.dao.CompactBlockDao
|
||||||
|
import cash.z.wallet.sdk.db.CompactBlockDb
|
||||||
|
import cash.z.wallet.sdk.ext.debug
|
||||||
|
import cash.z.wallet.sdk.jni.JniConverter
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.coroutines.channels.ConflatedBroadcastChannel
|
||||||
|
import kotlinx.coroutines.channels.ReceiveChannel
|
||||||
|
import rpc.CompactFormats
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Downloads compact blocks to the database and then scans them for transactions
|
||||||
|
*/
|
||||||
|
class Synchronizer(val applicationContext: Context, val scope: CoroutineScope, val birthday: Long = 373070L) : CompactBlockSource {
|
||||||
|
|
||||||
|
// TODO: convert to CompactBlockSource that just has a stream and then have the downloader operate on the stream
|
||||||
|
private val downloader = CompactBlockDownloader(scope)
|
||||||
|
private val savedBlockChannel = ConflatedBroadcastChannel<Result<CompactFormats.CompactBlock>>()
|
||||||
|
private lateinit var cacheDao: CompactBlockDao
|
||||||
|
private lateinit var cacheDb: CompactBlockDb
|
||||||
|
private lateinit var saveJob: Job
|
||||||
|
private lateinit var scanJob: Job
|
||||||
|
override fun blocks(): ReceiveChannel<Result<CompactFormats.CompactBlock>> = savedBlockChannel.openSubscription()
|
||||||
|
|
||||||
|
fun start() {
|
||||||
|
createDb()
|
||||||
|
downloader.start()
|
||||||
|
saveJob = saveBlocks()
|
||||||
|
scanJob = scanBlocks()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stop() {
|
||||||
|
scanJob.cancel()
|
||||||
|
saveJob.cancel()
|
||||||
|
downloader.stop()
|
||||||
|
cacheDb.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createDb() {
|
||||||
|
// TODO: inject the db and dao
|
||||||
|
cacheDb = Room.databaseBuilder(
|
||||||
|
applicationContext,
|
||||||
|
CompactBlockDb::class.java,
|
||||||
|
CACHEDB_NAME
|
||||||
|
)
|
||||||
|
.setJournalMode(RoomDatabase.JournalMode.TRUNCATE)
|
||||||
|
.fallbackToDestructiveMigration()
|
||||||
|
.build()
|
||||||
|
.apply { cacheDao = complactBlockDao() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveBlocks(): Job = scope.launch {
|
||||||
|
val downloadedBlockChannel = downloader.blocks()
|
||||||
|
while (isActive) {
|
||||||
|
try {
|
||||||
|
val nextBlock = downloadedBlockChannel.receive().getOrThrow()
|
||||||
|
cacheDao.insert(cash.z.wallet.sdk.vo.CompactBlock(nextBlock.height.toInt(), nextBlock.toByteArray()))
|
||||||
|
async {
|
||||||
|
savedBlockChannel.send(Result.success(nextBlock))
|
||||||
|
debug("stored block at height: ${nextBlock.height}")
|
||||||
|
}
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
debug("failed to store block due to $t")
|
||||||
|
async {
|
||||||
|
savedBlockChannel.send(Result.failure(t))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun scanBlocks(): Job = scope.launch {
|
||||||
|
val savedBlocks = blocks()
|
||||||
|
val converter = JniConverter()
|
||||||
|
converter.initLogs()
|
||||||
|
ScanResultDbCreator.create(applicationContext)
|
||||||
|
while (isActive) {
|
||||||
|
try {
|
||||||
|
debug("scanning blocks from $birthday onward...")
|
||||||
|
val nextBlock = savedBlocks.receive().getOrThrow() // wait until a block was saved
|
||||||
|
debug("...scanner observed a block (${nextBlock.height}) without crashing!")
|
||||||
|
delay(5000L)
|
||||||
|
val result = converter.scanBlocks(
|
||||||
|
applicationContext.getDatabasePath(CACHEDB_NAME).absolutePath,
|
||||||
|
applicationContext.getDatabasePath(ScanResultDbCreator.DB_NAME).absolutePath,
|
||||||
|
"dummyseed".toByteArray(),
|
||||||
|
birthday.toInt()
|
||||||
|
)
|
||||||
|
debug("scan complete")
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
debug("error while scanning blocks: $t")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val CACHEDB_NAME = "DownloadedCompactBlocks.db"
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,7 +15,7 @@ import cash.z.wallet.sdk.vo.Transaction
|
||||||
Block::class,
|
Block::class,
|
||||||
Note::class
|
Note::class
|
||||||
],
|
],
|
||||||
version = 1,
|
version = 2,
|
||||||
exportSchema = false
|
exportSchema = false
|
||||||
)
|
)
|
||||||
abstract class DerivedDataDb : RoomDatabase() {
|
abstract class DerivedDataDb : RoomDatabase() {
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package cash.z.wallet.sdk.ext
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
|
||||||
|
internal fun debug(message: String) {
|
||||||
|
Log.e("DBUG", message)
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package cash.z.wallet.sdk.ext
|
||||||
|
|
||||||
|
import rpc.Service
|
||||||
|
|
||||||
|
fun Long.toBlockHeight(): Service.BlockID = Service.BlockID.newBuilder().setHeight(this).build()
|
|
@ -6,7 +6,7 @@ import androidx.room.Entity
|
||||||
@Entity(primaryKeys = ["height"], tableName = "blocks")
|
@Entity(primaryKeys = ["height"], tableName = "blocks")
|
||||||
data class Block(
|
data class Block(
|
||||||
val height: Int,
|
val height: Int,
|
||||||
val time: Int,
|
val time: Int?,
|
||||||
@ColumnInfo(typeAffinity = ColumnInfo.BLOB, name = "sapling_tree")
|
@ColumnInfo(typeAffinity = ColumnInfo.BLOB, name = "sapling_tree")
|
||||||
val saplingTree: ByteArray
|
val saplingTree: ByteArray
|
||||||
) {
|
) {
|
||||||
|
@ -20,7 +20,7 @@ data class Block(
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
var result = height
|
var result = height
|
||||||
result = 31 * result + time
|
result = 31 * result + (time ?: 0)
|
||||||
result = 31 * result + saplingTree.contentHashCode()
|
result = 31 * result + saplingTree.contentHashCode()
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ data class Note(
|
||||||
|
|
||||||
val account: Int,
|
val account: Int,
|
||||||
val value: Int,
|
val value: Int,
|
||||||
val spent: Int,
|
val spent: Int?,
|
||||||
|
|
||||||
@ColumnInfo(typeAffinity = ColumnInfo.BLOB)
|
@ColumnInfo(typeAffinity = ColumnInfo.BLOB)
|
||||||
val diversifier: ByteArray,
|
val diversifier: ByteArray,
|
||||||
|
@ -69,7 +69,7 @@ data class Note(
|
||||||
result = 31 * result + outputIndex
|
result = 31 * result + outputIndex
|
||||||
result = 31 * result + account
|
result = 31 * result + account
|
||||||
result = 31 * result + value
|
result = 31 * result + value
|
||||||
result = 31 * result + spent
|
result = 31 * result + (spent ?: 0)
|
||||||
result = 31 * result + diversifier.contentHashCode()
|
result = 31 * result + diversifier.contentHashCode()
|
||||||
result = 31 * result + rcm.contentHashCode()
|
result = 31 * result + rcm.contentHashCode()
|
||||||
result = 31 * result + nf.contentHashCode()
|
result = 31 * result + nf.contentHashCode()
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package cash.z.wallet.sdk.proto;
|
syntax = "proto3";
|
||||||
|
package rpc;
|
||||||
message WalletData {
|
message WalletData {
|
||||||
required string name = 1;
|
string name = 1;
|
||||||
required int32 id = 2;
|
int32 id = 2;
|
||||||
optional string emails = 3;
|
string emails = 3;
|
||||||
}
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
package cash.z.wallet.sdk.rpc;
|
package cash.z.wallet.sdk.rpc;
|
||||||
option go_package = "walletrpc";
|
option go_package = "walletrpc";
|
||||||
|
package rpc;
|
||||||
|
|
||||||
// Remember that proto3 fields are all optional. A field that is not present will be set to its zero value.
|
// Remember that proto3 fields are all optional. A field that is not present will be set to its zero value.
|
||||||
// bytes fields of hashes are in canonical little-endian format.
|
// bytes fields of hashes are in canonical little-endian format.
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
package cash.z.wallet.sdk.rpc;
|
package cash.z.wallet.sdk.rpc;
|
||||||
option go_package = "walletrpc";
|
option go_package = "walletrpc";
|
||||||
|
package rpc;
|
||||||
|
|
||||||
import "compact_formats.proto";
|
import "compact_formats.proto";
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
package cash.z.wallet.sdk
|
||||||
|
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import io.grpc.ManagedChannel
|
||||||
|
import io.grpc.ManagedChannelBuilder
|
||||||
|
import org.junit.AfterClass
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertNotNull
|
||||||
|
import org.junit.BeforeClass
|
||||||
|
import org.junit.Test
|
||||||
|
import rpc.CompactTxStreamerGrpc
|
||||||
|
import rpc.Service
|
||||||
|
import rpc.Service.*
|
||||||
|
import rpc.WalletDataOuterClass
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
class GlueTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSanity_transactionParsing() {
|
||||||
|
val result =
|
||||||
|
blockingStub.getBlockRange(
|
||||||
|
BlockRange.newBuilder()
|
||||||
|
.setStart(heightOf(373070))
|
||||||
|
.setEnd(heightOf(373085))
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
assertNotNull(result)
|
||||||
|
assertEquals(372950, result.next().height)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun heightOf(height: Long): Service.BlockID {
|
||||||
|
return BlockID.newBuilder().setHeight(height).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
lateinit var blockingStub: CompactTxStreamerGrpc.CompactTxStreamerBlockingStub
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
@JvmStatic
|
||||||
|
fun setup() {
|
||||||
|
val channel = ManagedChannelBuilder.forAddress("localhost", 9067).usePlaintext().build()
|
||||||
|
blockingStub = CompactTxStreamerGrpc.newBlockingStub(channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
@JvmStatic
|
||||||
|
fun tearDown() {
|
||||||
|
(blockingStub.channel as ManagedChannel).shutdown().awaitTermination(2000L, TimeUnit.MILLISECONDS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
package cash.z.wallet.sdk
|
||||||
|
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import io.grpc.ManagedChannel
|
||||||
|
import io.grpc.ManagedChannelBuilder
|
||||||
|
import org.junit.AfterClass
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertNotNull
|
||||||
|
import org.junit.BeforeClass
|
||||||
|
import org.junit.Test
|
||||||
|
import rpc.CompactTxStreamerGrpc
|
||||||
|
import rpc.Service
|
||||||
|
import rpc.Service.*
|
||||||
|
import rpc.WalletDataOuterClass
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
class GrpcTest {
|
||||||
|
@Test
|
||||||
|
fun testSanity_protoFilesCreated() {
|
||||||
|
val name = "Zooko"
|
||||||
|
val data = WalletDataOuterClass.WalletData.newBuilder()
|
||||||
|
.setName(name)
|
||||||
|
.setId(1)
|
||||||
|
.build()
|
||||||
|
assertEquals(name, data.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSanity_serviceCreated() {
|
||||||
|
val result = blockingStub.getLatestBlock(ChainSpec.newBuilder().build())
|
||||||
|
assertNotNull(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSanity_transactionParsing() {
|
||||||
|
val result =
|
||||||
|
blockingStub.getBlockRange(
|
||||||
|
BlockRange.newBuilder()
|
||||||
|
.setStart(heightOf(372950))
|
||||||
|
.setEnd(heightOf(372954))
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
assertNotNull(result)
|
||||||
|
assertEquals(372950, result.next().height)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun heightOf(height: Long): Service.BlockID {
|
||||||
|
return BlockID.newBuilder().setHeight(height).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
lateinit var blockingStub: CompactTxStreamerGrpc.CompactTxStreamerBlockingStub
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
@JvmStatic
|
||||||
|
fun setup() {
|
||||||
|
val channel = ManagedChannelBuilder.forAddress("localhost", 9067).usePlaintext().build()
|
||||||
|
blockingStub = CompactTxStreamerGrpc.newBlockingStub(channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
@JvmStatic
|
||||||
|
fun tearDown() {
|
||||||
|
(blockingStub.channel as ManagedChannel).shutdown().awaitTermination(2000L, TimeUnit.MILLISECONDS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
task unregistered {
|
||||||
|
println "configuring unregistered"
|
||||||
|
doLast {
|
||||||
|
println 'unregistered'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("pb") {
|
||||||
|
println "configuring pb"
|
||||||
|
doLast {
|
||||||
|
println 'preBuild'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tasks.register("generateJni") {
|
||||||
|
println "configuring generateJni"
|
||||||
|
doLast {
|
||||||
|
println 'jni'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tasks.register("copyA") {
|
||||||
|
dependsOn generateJni
|
||||||
|
println "configuring copyA"
|
||||||
|
doLast {
|
||||||
|
println 'copyA'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tasks.register("copyB") {
|
||||||
|
dependsOn generateJni
|
||||||
|
println "configuring copyB"
|
||||||
|
doLast {
|
||||||
|
println 'copyB'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tasks.register("copyC") {
|
||||||
|
dependsOn generateJni
|
||||||
|
println "configuring copyC"
|
||||||
|
doLast {
|
||||||
|
println 'copyC'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task copyAll {
|
||||||
|
dependsOn copyA, copyB, copyC
|
||||||
|
}
|
||||||
|
|
||||||
|
pb.dependsOn copyAll
|
Loading…
Reference in New Issue