
io.p8e.client.EngineClient.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of p8e-sdk Show documentation
Show all versions of p8e-sdk Show documentation
A collection of services and libraries that iteract and run Provenance Java based contracts.
package io.p8e.client
import com.google.common.cache.Cache
import com.google.common.cache.CacheBuilder
import com.google.protobuf.Message
import com.google.protobuf.util.Timestamps
import io.grpc.ManagedChannel
import io.grpc.stub.StreamObserver
import io.p8e.engine.extension.withAudience
import io.p8e.grpc.client.*
import io.p8e.index.client.IndexClient
import io.p8e.proto.Affiliate
import io.p8e.proto.Affiliate.AffiliateContractWhitelist
import io.p8e.proto.Common.Location
import io.p8e.proto.ContractScope.*
import io.p8e.proto.ContractSpecs.ContractSpec
import io.p8e.proto.ContractSpecs.ScopeSpec
import io.p8e.proto.Domain.SpecMapping
import io.p8e.proto.Domain.SpecRequest
import io.p8e.proto.Envelope.EnvelopeEvent
import io.p8e.proto.Envelope.EnvelopeEvent.Action
import io.p8e.proto.Index.FactHistoryRequest
import io.p8e.proto.Index.FactHistoryResponse
import io.p8e.proto.PK
import io.p8e.spec.P8eContract
import io.p8e.util.ProtoParseException
import io.p8e.util.orThrow
import io.p8e.util.toPublicKeyProto
import io.p8e.util.toOffsetDateTimeProv
import io.p8e.util.toProtoTimestampProv
import io.p8e.util.toProtoUuidProv
import io.provenance.p8e.encryption.ecies.ECUtils
import java.io.InputStream
import java.lang.reflect.Method
import java.security.PublicKey
import java.time.OffsetDateTime
import java.util.UUID
import java.util.concurrent.TimeUnit
interface P8eClient {
fun addSpec(contractSpec: List, specMapping: List, scopeSpec: List)
fun register(enc: Affiliate.AffiliateRegisterRequest)
fun whitelistClass(whitelist: AffiliateContractWhitelist)
fun getContracts(groupUuid: UUID): EnvelopeCollection
fun getContract(executionUuid: UUID): Envelope
fun getScopeSnapshot(executionUuid: UUID): Scope
fun reject(executionUuid: UUID, message: String): Envelope
fun cancel(executionUuid: UUID, message: String): Envelope
fun event(
clazz: Class,
inObserver: StreamObserver
): StreamObserver
fun execute(
request: EnvelopeEvent
): EnvelopeEvent
fun loadProto(uri: String, clazz: Class): T
fun saveProto(msg: T, executionUuid: UUID?, audience: Set = setOf()): Location
fun storeObject(inputStream: InputStream, audience: Set): Location
fun loadObject(hash: String): ByteArray
fun loadProto(
hash: String,
classname: String
): Message
fun loadProto(
bytes: ByteArray,
classname: String
): Message
fun loadProtoJson(hash: String, classname: String, contractSpecHash: String): String
fun getFactHistory(
scopeUuid: UUID,
factName: String,
classname: String,
startWindow: OffsetDateTime = Timestamps.MIN_VALUE.toOffsetDateTimeProv(),
endWindow: OffsetDateTime = Timestamps.MAX_VALUE.toOffsetDateTimeProv()
): FactHistoryResponse
}
// TODO - envar the threadpool size
abstract class EventMonitorClient(
protected val publicKey: PublicKey,
protected val signingAndEncryptionPublicKeysClient: SigningAndEncryptionPublicKeysClient,
protected val chaincodeClient: ChaincodeClient,
protected val envelopeClient: EnvelopeClient,
protected val indexClient: IndexClient,
protected val objectClient: ObjectClient
): P8eClient {
override fun addSpec(
contractSpec: List,
specMapping: List,
scopeSpec: List,
) {
chaincodeClient.addSpec(
SpecRequest.newBuilder()
.addAllContractSpec(contractSpec)
.addAllSpecMapping(specMapping)
.addAllScopeSpec(scopeSpec)
.build()
)
}
override fun register(enc: Affiliate.AffiliateRegisterRequest) = signingAndEncryptionPublicKeysClient.register(enc)
override fun whitelistClass(whitelist: AffiliateContractWhitelist) = signingAndEncryptionPublicKeysClient.whitelistClass(whitelist)
override fun getContracts(groupUuid: UUID): EnvelopeCollection = envelopeClient.getAllByGroupUuid(groupUuid)
override fun getContract(executionUuid: UUID): Envelope = envelopeClient.getByExecutionUuid(executionUuid)
override fun getScopeSnapshot(executionUuid: UUID): Scope = envelopeClient.getScopeByExecutionUuid(executionUuid)
override fun reject(executionUuid: UUID, message: String): Envelope = envelopeClient.rejectByExecutionUuid(executionUuid, message)
override fun cancel(executionUuid: UUID, message: String): Envelope = envelopeClient.cancelByExecutionUuid(executionUuid, message)
override fun event(
clazz: Class,
inObserver: StreamObserver
): StreamObserver {
val outObserver = envelopeClient.event(inObserver)
val event = EnvelopeEvent.newBuilder()
.setAction(Action.CONNECT)
.setPublicKey(
PK.SigningAndEncryptionPublicKeys.newBuilder()
.setSigningPublicKey(publicKey.toPublicKeyProto())
.build()
)
.setClassname(clazz.name)
.build()
// Start subscription by sending affiliate/cert/class
outObserver.onNext(
event
)
return outObserver
}
override fun execute(request: EnvelopeEvent): EnvelopeEvent {
return envelopeClient.execute(request)
}
override fun loadProto(uri: String, clazz: Class): T =
loadProto(uri, clazz.name) as T
override fun saveProto(msg: T, executionUuid: UUID?, audience: Set): Location =
objectClient.store(msg.withAudience(audience.map(ECUtils::convertPublicKeyToBytes).toSet()))
override fun loadProto(
hash: String,
classname: String
): Message {
return loadProto(
loadObject(hash),
classname
)
}
override fun loadProto(
bytes: ByteArray,
classname: String
): Message {
val clazz = Message::class.java
val instanceOfThing = parseFromLookup(classname)
.invoke(
null,
bytes
)
if (!clazz.isAssignableFrom(instanceOfThing.javaClass)) {
throw ProtoParseException("Unable to assign instance ${instanceOfThing::class.java.name} to type ${clazz.name}")
}
return clazz.cast(instanceOfThing)
}
override fun loadProtoJson(hash: String, classname: String, contractSpecHash: String): String = objectClient.loadJson(hash, classname, contractSpecHash)
override fun storeObject(
inputStream: InputStream,
audience: Set
): Location {
return objectClient.store(
inputStream.use {
it.readAllBytes()
}.withAudience(audience.map(ECUtils::convertPublicKeyToBytes).toSet())
)
}
override fun loadObject(
hash: String
): ByteArray {
return byteCache.asMap().computeIfAbsent(hash) {
objectClient.load(hash)
}
}
override fun getFactHistory(
scopeUuid: UUID,
factName: String,
classname: String,
startWindow: OffsetDateTime,
endWindow: OffsetDateTime
): FactHistoryResponse {
return indexClient.factHistory(
FactHistoryRequest.newBuilder()
.setScopeUuid(scopeUuid.toProtoUuidProv())
.setFactName(factName)
.setClassname(classname)
.setStartWindow(startWindow.toProtoTimestampProv())
.setEndWindow(endWindow.toProtoTimestampProv())
.build()
)
}
companion object {
private var byteCache: Cache = CacheBuilder.newBuilder()
.maximumSize(128 * 1000) // 128MB max size assuming facts average under 1KB
.expireAfterWrite(5, TimeUnit.MINUTES)
.build()
private var parseFromCache: Cache = CacheBuilder.newBuilder()
.maximumSize(128 * 1000) // 128MB max size assuming facts average under 1KB
.expireAfterWrite(5, TimeUnit.MINUTES)
.build()
private fun parseFromLookup(classname: String): Method {
return parseFromCache.asMap().computeIfAbsent(classname) {
javaClass.classLoader.loadClass(classname)
.declaredMethods.find {
it.returnType.name == classname && it.name == "parseFrom" && it.parameterCount == 1 && it.parameters[0].type == ByteArray::class.java
}.orThrow { ProtoParseException("Unable to find \"parseFrom\" method on $classname") }
}
}
}
}
class RemoteClient(
publicKey: PublicKey,
channel: ManagedChannel,
challengeResponseInterceptor: ChallengeResponseInterceptor,
indexClient: IndexClient,
deadlineMs: Long
): EventMonitorClient(
publicKey,
SigningAndEncryptionPublicKeysClient(channel, challengeResponseInterceptor, deadlineMs),
ChaincodeClient(channel, challengeResponseInterceptor, deadlineMs),
EnvelopeClient(channel, challengeResponseInterceptor, deadlineMs),
indexClient,
ObjectClient(channel, challengeResponseInterceptor, deadlineMs)
)
© 2015 - 2025 Weber Informatics LLC | Privacy Policy