Create synchronizer and add layer of business logic onto the data layer

App is functional and displays transactions
This commit is contained in:
Kevin Gorham 2019-01-03 00:31:12 -05:00
parent 1501f1a7d2
commit 6c869a47df
27 changed files with 6845 additions and 50 deletions

View File

@ -5,8 +5,10 @@ buildscript {
'targetSdkVersion': 28
]
ext.versions = [
'architectureComponents': '2.0.0',
'grpc':'1.17.1',
'kotlin': '1.3.10',
'architectureComponents': '2.0.0'
'coroutines': '1.1.0'
]
repositories {
google()
@ -17,6 +19,7 @@ buildscript {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
classpath "com.github.ben-manes:gradle-versions-plugin:0.20.0"
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"
apply plugin: 'kotlin-kapt'
apply plugin: 'com.google.protobuf'
apply plugin: 'com.github.ben-manes.versions'
apply plugin: 'com.github.dcendents.android-maven'
@ -34,6 +38,7 @@ version = '1.2.4'
repositories {
google()
jcenter()
mavenCentral()
}
android {
@ -42,9 +47,10 @@ android {
defaultConfig {
minSdkVersion 16
targetSdkVersion 28
versionCode = 1_02_03
versionName = "1.2.3"
versionCode = 1_03_00
versionName = "1.3.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled false
}
buildTypes {
@ -56,7 +62,10 @@ android {
sourceSets {
main {
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 {
// Square
api "com.squareup.wire:wire-runtime:2.2.0"
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.multidex:multidex:2.0.1'
// Architecture components
implementation "androidx.lifecycle:lifecycle-runtime:${versions.architectureComponents}"
implementation "androidx.lifecycle:lifecycle-extensions:${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.room:room-compiler:${versions.architectureComponents}"
// Other
// 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
testImplementation "junit:junit:4.12"
androidTestImplementation "androidx.test:runner:1.1.0"
androidTestImplementation "androidx.test.espresso:espresso-core:3.1.0"
androidTestImplementation "androidx.test:core:1.0.0"
androidTestImplementation "androidx.test:runner:1.1.1"
androidTestImplementation "androidx.test.espresso:espresso-core:3.1.1"
androidTestImplementation "androidx.test:core:1.1.0"
androidTestImplementation "androidx.arch.core:core-testing:${versions.architectureComponents}"
}
preBuild.dependsOn generateProtobufClasses
preBuild.dependsOn includeDirBugFix
preBuild.dependsOn copyAllJniLibs

View File

@ -1,5 +1,6 @@
def protoSrcDir = "src/main/proto"
def protoDestDir = "build/generated/source/wire"
//def protoSrcDir = "src/main/proto"
//def protoDestDir = "build/generated/source/grpc"
def protoIncludeDir = "build/extracted-include-protos/main"
def jniSrcDir = "src/main/rust"
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 libArmeabiDir = "src/main/jniLibs/armeabi-v7a"
buildscript {
dependencies {
classpath("com.squareup.wire:wire-compiler:2.2.0")
}
repositories {
mavenCentral()
}
}
//buildscript {
// dependencies {
// classpath("com.squareup.wire:wire-compiler:2.2.0")
// }
// repositories {
// 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 {
println("**** CREATING PROTOS ****")
delete(protoDestDir)
mkdir(protoDestDir)
mkdir(protoIncludeDir)
}
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") {
doFirst {
println("**** CREATING JNI LIBS ****")

View File

@ -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")
}
}
}

View File

@ -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()
}
}
}

View File

@ -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)
}
}
}

View File

@ -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)
}
}
}

View File

@ -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

View File

@ -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)
}

View File

@ -1,2 +1,6 @@
<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>

View File

@ -5,7 +5,7 @@ import cash.z.wallet.sdk.vo.CompactBlock
@Dao
interface CompactBlockDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(block: CompactBlock)
@Query("SELECT * FROM compactblocks WHERE height = :height")

View File

@ -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
}
}
}

View File

@ -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>>
}

View File

@ -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()
}
}
}

View File

@ -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"
}
}

View File

@ -15,7 +15,7 @@ import cash.z.wallet.sdk.vo.Transaction
Block::class,
Note::class
],
version = 1,
version = 2,
exportSchema = false
)
abstract class DerivedDataDb : RoomDatabase() {

View File

@ -0,0 +1,7 @@
package cash.z.wallet.sdk.ext
import android.util.Log
internal fun debug(message: String) {
Log.e("DBUG", message)
}

View File

@ -0,0 +1,5 @@
package cash.z.wallet.sdk.ext
import rpc.Service
fun Long.toBlockHeight(): Service.BlockID = Service.BlockID.newBuilder().setHeight(this).build()

View File

@ -6,7 +6,7 @@ import androidx.room.Entity
@Entity(primaryKeys = ["height"], tableName = "blocks")
data class Block(
val height: Int,
val time: Int,
val time: Int?,
@ColumnInfo(typeAffinity = ColumnInfo.BLOB, name = "sapling_tree")
val saplingTree: ByteArray
) {
@ -20,7 +20,7 @@ data class Block(
override fun hashCode(): Int {
var result = height
result = 31 * result + time
result = 31 * result + (time ?: 0)
result = 31 * result + saplingTree.contentHashCode()
return result
}

View File

@ -33,7 +33,7 @@ data class Note(
val account: Int,
val value: Int,
val spent: Int,
val spent: Int?,
@ColumnInfo(typeAffinity = ColumnInfo.BLOB)
val diversifier: ByteArray,
@ -69,7 +69,7 @@ data class Note(
result = 31 * result + outputIndex
result = 31 * result + account
result = 31 * result + value
result = 31 * result + spent
result = 31 * result + (spent ?: 0)
result = 31 * result + diversifier.contentHashCode()
result = 31 * result + rcm.contentHashCode()
result = 31 * result + nf.contentHashCode()

View File

@ -1,6 +1,7 @@
package cash.z.wallet.sdk.proto;
syntax = "proto3";
package rpc;
message WalletData {
required string name = 1;
required int32 id = 2;
optional string emails = 3;
string name = 1;
int32 id = 2;
string emails = 3;
}

View File

@ -1,6 +1,7 @@
syntax = "proto3";
package cash.z.wallet.sdk.rpc;
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.
// bytes fields of hashes are in canonical little-endian format.

View File

@ -1,6 +1,7 @@
syntax = "proto3";
package cash.z.wallet.sdk.rpc;
option go_package = "walletrpc";
package rpc;
import "compact_formats.proto";

View File

@ -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)
}
}
}

View File

@ -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)
}
}
}

46
testing.gradle Executable file
View File

@ -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