From 867cede40957c958603072d44e44242d262351fe Mon Sep 17 00:00:00 2001 From: Wyatt Date: Mon, 21 Jun 2021 08:10:57 -0700 Subject: [PATCH 01/15] Working on adding data access list to the contract scope --- .../engine/service/ChaincodeInvokeService.kt | 38 +++++++++++++------ .../engine/service/EnvelopeService.kt | 2 + .../engine/util/ProvenanceProtoExtensions.kt | 12 ++++++ .../src/main/proto/p8e/contract_scope.proto | 2 + .../p8e/shared/service/AffiliateService.kt | 2 +- 5 files changed, 43 insertions(+), 13 deletions(-) diff --git a/p8e-api/src/main/kotlin/io/provenance/engine/service/ChaincodeInvokeService.kt b/p8e-api/src/main/kotlin/io/provenance/engine/service/ChaincodeInvokeService.kt index 80e38746..af7688b6 100644 --- a/p8e-api/src/main/kotlin/io/provenance/engine/service/ChaincodeInvokeService.kt +++ b/p8e-api/src/main/kotlin/io/provenance/engine/service/ChaincodeInvokeService.kt @@ -13,15 +13,14 @@ import io.provenance.p8e.shared.extension.logger import io.provenance.engine.config.ChaincodeProperties import io.provenance.engine.crypto.Account import io.provenance.engine.domain.TransactionStatusRecord +import io.provenance.engine.util.PROV_METADATA_PREFIX_SCOPE_ADDR +import io.provenance.engine.util.toAddress import io.provenance.engine.util.toProv -import io.provenance.metadata.v1.Description -import io.provenance.metadata.v1.MsgP8eMemorializeContractRequest -import io.provenance.metadata.v1.MsgWriteP8eContractSpecRequest -import io.provenance.metadata.v1.MsgWriteScopeSpecificationRequest -import io.provenance.metadata.v1.ScopeSpecification +import io.provenance.metadata.v1.* import io.provenance.p8e.shared.domain.ContractSpecificationRecord import io.provenance.p8e.shared.domain.ContractTxResult import io.provenance.p8e.shared.domain.ScopeSpecificationRecord +import io.provenance.p8e.shared.service.AffiliateService import io.provenance.pbc.clients.* import org.jetbrains.exposed.sql.transactions.transaction import org.springframework.stereotype.Component @@ -40,7 +39,8 @@ data class ContractRequestWrapper( val executionUuid: UUID, val request: MsgP8eMemorializeContractRequest, val future: CompletableFuture, -) + val addDataAccessRequest: MsgAddScopeDataAccessRequest?, + ) @Component class ChaincodeInvokeService( @@ -48,6 +48,7 @@ class ChaincodeInvokeService( private val transactionStatusService: TransactionStatusService, private val chaincodeProperties: ChaincodeProperties, private val provenanceGrpc: ProvenanceGrpcService, + private val affiliateService: AffiliateService, ) : IChaincodeInvokeService { private val log = logger() @@ -180,10 +181,15 @@ class ChaincodeInvokeService( log.debug("Internal structures\nblockScopeIds: $blockScopeIds\npriorityFutureScopeToQueue: ${priorityScopeBacklog.entries.map { e -> "${e.key} => ${e.value.size}"}}") try { - val txBody = batch.map { + val txBody = batch.flatMap { it.attempts++ - it.request + //TODO this might break the error index matching + listOf(it.request, it.addDataAccessRequest).filterNotNull() }.toTxBody() +// val txBody = batch.map { +// it.attempts++ +// it.request +// }.toTxBody() // Send the transactions to the blockchain. val resp = synchronized(provenanceGrpc) { batchTx(txBody) } @@ -218,7 +224,6 @@ class ChaincodeInvokeService( decrementSequenceNumber() log.warn("Unexpected chain execution error", t) val errorMessage = t.message ?: "Unexpected chain execution error" - val retryable = t.message?.contains("account sequence mismatch") == true val matchIndex = t.message?.matchIndex() @@ -229,7 +234,8 @@ class ChaincodeInvokeService( retryable -> { handleBatchRetry(errorMessage) } - else -> { // fail the whole batch + + else -> { // fail the whole batch batch.map { it.future.completeExceptionally(t) it.executionUuid @@ -344,11 +350,19 @@ class ChaincodeInvokeService( Contracts.ContractType.CHANGE_SCOPE, Contracts.ContractType.UNRECOGNIZED -> throw IllegalStateException("Unrecognized contract type of ${env.contract.typeValue} for envelope ${env.executionUuid.value}") } - val future = CompletableFuture() + val scopeDataAccessRequest = MsgAddScopeDataAccessRequest.newBuilder() + .addAllDataAccess(env.affiliateSharesList.map { + affiliateService.getAddress( + it.toPublicKey(), chaincodeProperties.mainNet + ) + }) + .addSigners(accountProvider.bech32Address()) + .setScopeId(env.ref.scopeUuid.value.toUuidProv().toAddress(PROV_METADATA_PREFIX_SCOPE_ADDR).toByteString()) + .build().takeIf { env.affiliateSharesList.isNotEmpty() } // TODO what to do when this offer fails? - queue.offer(ContractRequestWrapper(env.executionUuid.toUuidProv(), msg, future)) + queue.offer(ContractRequestWrapper(env.executionUuid.toUuidProv(), msg, future, scopeDataAccessRequest)) return future } diff --git a/p8e-api/src/main/kotlin/io/provenance/engine/service/EnvelopeService.kt b/p8e-api/src/main/kotlin/io/provenance/engine/service/EnvelopeService.kt index cdceafbc..153becb7 100644 --- a/p8e-api/src/main/kotlin/io/provenance/engine/service/EnvelopeService.kt +++ b/p8e-api/src/main/kotlin/io/provenance/engine/service/EnvelopeService.kt @@ -60,8 +60,10 @@ class EnvelopeService( val signingKeyPair = affiliateService.getSigningKeyPair(publicKey) val pen = Pen(signingKeyPair.private, signingKeyPair.public) + val affiliateShares = affiliateService.getShares(publicKey); // Update the envelope for invoker and recitals with correct signing and encryption keys. val envelope = env.toBuilder() + .addAllAffiliateShares(affiliateShares.map{ it.publicKey.toPublicKeyProto() }) .apply { if(env.contract.startTime == Timestamp.getDefaultInstance()) { contractBuilder.startTime = OffsetDateTime.now().toProtoTimestampProv() diff --git a/p8e-api/src/main/kotlin/io/provenance/engine/util/ProvenanceProtoExtensions.kt b/p8e-api/src/main/kotlin/io/provenance/engine/util/ProvenanceProtoExtensions.kt index 316fff91..6e393249 100644 --- a/p8e-api/src/main/kotlin/io/provenance/engine/util/ProvenanceProtoExtensions.kt +++ b/p8e-api/src/main/kotlin/io/provenance/engine/util/ProvenanceProtoExtensions.kt @@ -22,8 +22,11 @@ import org.kethereum.crypto.decompressKey import org.kethereum.model.PublicKey import java.lang.Long.max import java.math.BigInteger +import java.nio.ByteBuffer +import java.util.* const val PROV_METADATA_PREFIX_CONTRACT_SPEC: Byte = 0x03 +const val PROV_METADATA_PREFIX_SCOPE_ADDR: Byte = 0x00 fun PartyType.toProv() = when (this) { PartyType.SERVICER -> ProvenancePartyType.PARTY_TYPE_SERVICER @@ -50,6 +53,15 @@ fun ContractSpec.toProvHash(): String { return String(provHash.base64Encode()) } +fun UUID.asBytes(): ByteArray { + val b = ByteBuffer.wrap(ByteArray(16)) + b.putLong(mostSignificantBits) + b.putLong(leastSignificantBits) + return b.array() +} + +fun UUID.toAddress(prefix: Byte): ByteArray = (listOf(prefix) + this.asBytes().toList()).toByteArray() + fun ContractSpec.toProv(): io.provenance.metadata.v1.p8e.ContractSpec = io.provenance.metadata.v1.p8e.ContractSpec.parseFrom(toByteArray()) fun Contracts.Contract.toProv(): io.provenance.metadata.v1.p8e.Contract = io.provenance.metadata.v1.p8e.Contract.parseFrom(toByteArray()).run { diff --git a/p8e-proto-internal/src/main/proto/p8e/contract_scope.proto b/p8e-proto-internal/src/main/proto/p8e/contract_scope.proto index 2b91e0dd..ab0e1d07 100644 --- a/p8e-proto-internal/src/main/proto/p8e/contract_scope.proto +++ b/p8e-proto-internal/src/main/proto/p8e/contract_scope.proto @@ -50,6 +50,8 @@ message Envelope { // Scope snapshot for executing Scope scope = 8; + repeated PublicKey affiliate_shares = 10; + Status status = 9; enum Status { diff --git a/p8e-shared/src/main/kotlin/io/provenance/p8e/shared/service/AffiliateService.kt b/p8e-shared/src/main/kotlin/io/provenance/p8e/shared/service/AffiliateService.kt index 4fa3f8f2..600f9bd5 100644 --- a/p8e-shared/src/main/kotlin/io/provenance/p8e/shared/service/AffiliateService.kt +++ b/p8e-shared/src/main/kotlin/io/provenance/p8e/shared/service/AffiliateService.kt @@ -106,7 +106,7 @@ class AffiliateService( } @Cacheable(PUBLIC_KEY_TO_ADDRESS) - private fun getAddress(publicKey: PublicKey, mainNet: Boolean): String = + fun getAddress(publicKey: PublicKey, mainNet: Boolean): String = publicKey.let { (it as BCECPublicKey).q.getEncoded(true) }.let { From c84868e3d23f94ea70d7d8885505ff63a90824fe Mon Sep 17 00:00:00 2001 From: Wyatt Date: Mon, 21 Jun 2021 14:20:19 -0700 Subject: [PATCH 02/15] Hardcoding version forcing function --- .../engine/service/ChaincodeInvokeService.kt | 34 +++++++++++--- .../engine/service/ProvenanceGrpcService.kt | 44 +++++++++++------- .../src/main/kotlin/io/p8e/crypto/PBSigner.kt | 45 ++++++++++++++++++- p8e-shared/build.gradle | 25 ++++++++++- 4 files changed, 123 insertions(+), 25 deletions(-) diff --git a/p8e-api/src/main/kotlin/io/provenance/engine/service/ChaincodeInvokeService.kt b/p8e-api/src/main/kotlin/io/provenance/engine/service/ChaincodeInvokeService.kt index af7688b6..5204d178 100644 --- a/p8e-api/src/main/kotlin/io/provenance/engine/service/ChaincodeInvokeService.kt +++ b/p8e-api/src/main/kotlin/io/provenance/engine/service/ChaincodeInvokeService.kt @@ -12,6 +12,8 @@ import io.p8e.util.* import io.provenance.p8e.shared.extension.logger import io.provenance.engine.config.ChaincodeProperties import io.provenance.engine.crypto.Account +import io.provenance.engine.crypto.asBech32PublicKey +import io.provenance.engine.crypto.toSignerMeta import io.provenance.engine.domain.TransactionStatusRecord import io.provenance.engine.util.PROV_METADATA_PREFIX_SCOPE_ADDR import io.provenance.engine.util.toAddress @@ -23,7 +25,9 @@ import io.provenance.p8e.shared.domain.ScopeSpecificationRecord import io.provenance.p8e.shared.service.AffiliateService import io.provenance.pbc.clients.* import org.jetbrains.exposed.sql.transactions.transaction +import org.kethereum.crypto.toAddress import org.springframework.stereotype.Component +import java.security.KeyPair import java.time.OffsetDateTime import java.util.* import java.util.concurrent.CompletableFuture @@ -55,7 +59,7 @@ class ChaincodeInvokeService( private val objectMapper = ObjectMapper().configureProvenance() private val indexRegex = "^.*message index: (\\d+).*$".toRegex() - private val accountInfo = provenanceGrpc.accountInfo() + private val accountInfo = provenanceGrpc.accountInfo("tp1vz99nyd2er8myeugsr4xm5duwhulhp5arsr9wt") // Optional gas multiplier tracking private var gasMultiplierResetAt = OffsetDateTime.now() @@ -181,13 +185,17 @@ class ChaincodeInvokeService( log.debug("Internal structures\nblockScopeIds: $blockScopeIds\npriorityFutureScopeToQueue: ${priorityScopeBacklog.entries.map { e -> "${e.key} => ${e.value.size}"}}") try { + log.info("*this is the account info^") + log.info(accountInfo.toString()) val txBody = batch.flatMap { it.attempts++ //TODO this might break the error index matching - listOf(it.request, it.addDataAccessRequest).filterNotNull() +// listOf(it.request, it.addDataAccessRequest).filterNotNull() + listOf(it.addDataAccessRequest).filterNotNull() }.toTxBody() // val txBody = batch.map { // it.attempts++ +// //TODO this might break the error index matching // it.request // }.toTxBody() @@ -351,6 +359,7 @@ class ChaincodeInvokeService( Contracts.ContractType.UNRECOGNIZED -> throw IllegalStateException("Unrecognized contract type of ${env.contract.typeValue} for envelope ${env.executionUuid.value}") } val future = CompletableFuture() +// TODO COMPLETE THIS Set up dataAccessMsg to be offered to the queue. Can be null. val scopeDataAccessRequest = MsgAddScopeDataAccessRequest.newBuilder() .addAllDataAccess(env.affiliateSharesList.map { @@ -358,10 +367,23 @@ class ChaincodeInvokeService( it.toPublicKey(), chaincodeProperties.mainNet ) }) - .addSigners(accountProvider.bech32Address()) +// .addSigners(accountProvider.bech32Address()) + .addAllSigners(env.signaturesList.map { + it.signer.signingPublicKey.toPublicKey().let { + affiliateService.getAddress( it, chaincodeProperties.mainNet) + } + }) .setScopeId(env.ref.scopeUuid.value.toUuidProv().toAddress(PROV_METADATA_PREFIX_SCOPE_ADDR).toByteString()) .build().takeIf { env.affiliateSharesList.isNotEmpty() } + + // Later, check the scope and add if it doesn't already exist. + // When get to the point of creating that message, ask pierce for converting to provenance address. // TODO what to do when this offer fails? + log.info("Below is scope uuid") + log.info("THIS IS THE MESSAGE THAT IS BEING ADDED TO THE QUEUE") + log.info(msg.toString()) + log.info("###########################################################") + log.info(scopeDataAccessRequest.toString()) queue.offer(ContractRequestWrapper(env.executionUuid.toUuidProv(), msg, future, scopeDataAccessRequest)) return future @@ -428,7 +450,7 @@ class ChaincodeInvokeService( fun batchTx(body: TxBody, applyMultiplier: Boolean = true): BroadcastTxResponse { val accountNumber = accountInfo.accountNumber - val sequenceNumber = getAndIncrementSequenceNumber() + val sequenceNumber = 0L//getAndIncrementSequenceNumber() val estimate = provenanceGrpc.estimateTx(body, accountNumber, sequenceNumber) @@ -441,8 +463,8 @@ class ChaincodeInvokeService( } else { log.info("skipping gasMultiplier due to daily limit") } - - return provenanceGrpc.batchTx(body, accountNumber, sequenceNumber, estimate) + var affiliateKeyPair = KeyPair("0A41046C57E9E25101D5E553AE003E2F79025E389B51495607C796B4E95C0A94001FBC24D84CD0780819612529B803E8AD0A397F474C965D957D33DD64E642B756FBC4".toJavaPublicKey(), "0A2071E487C3FB00642F1863D57749F32D94F13892FA68D02049F7EA9E8FC58D6E63".toJavaPrivateKey()) + return provenanceGrpc.batchTx(body, accountNumber, sequenceNumber, estimate, affiliateKeyPair.toSignerMeta()) } /** diff --git a/p8e-api/src/main/kotlin/io/provenance/engine/service/ProvenanceGrpcService.kt b/p8e-api/src/main/kotlin/io/provenance/engine/service/ProvenanceGrpcService.kt index d78d06a8..a5b5ef4c 100644 --- a/p8e-api/src/main/kotlin/io/provenance/engine/service/ProvenanceGrpcService.kt +++ b/p8e-api/src/main/kotlin/io/provenance/engine/service/ProvenanceGrpcService.kt @@ -1,6 +1,7 @@ package io.provenance.engine.service import com.google.protobuf.Any +import com.google.protobuf.ByteString import com.google.protobuf.Message import cosmos.auth.v1beta1.Auth import cosmos.auth.v1beta1.QueryGrpc @@ -17,18 +18,26 @@ import io.p8e.engine.threadedMap import io.p8e.proto.ContractScope import io.p8e.util.ThreadPoolFactory import io.p8e.util.toByteString +import io.p8e.util.toPublicKeyProto import io.provenance.engine.config.ChaincodeProperties import io.provenance.engine.crypto.Account import io.provenance.engine.crypto.PbSigner +import io.provenance.engine.crypto.SignerFn +import io.provenance.engine.crypto.SignerMeta +import io.provenance.engine.crypto.toSignerMeta import io.provenance.engine.util.toP8e import io.provenance.metadata.v1.ContractSpecificationRequest import io.provenance.metadata.v1.ScopeRequest import io.provenance.p8e.shared.extension.logger import io.provenance.p8e.shared.service.AffiliateService import io.provenance.pbc.clients.roundUp +import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey import org.kethereum.crypto.getCompressedPublicKey +import org.kethereum.model.ECKeyPair +import org.kethereum.model.PublicKey import org.springframework.stereotype.Service import java.net.URI +import java.security.KeyPair import java.util.concurrent.TimeUnit import java.util.logging.Level import java.util.logging.Logger @@ -44,10 +53,13 @@ class ProvenanceGrpcService( ) { companion object { val executor = ThreadPoolFactory.newFixedThreadPool(5, "prov-grpc-%d") + val DIRECT_SIGN_MODE = ModeInfo.newBuilder().setSingle( + ModeInfo.Single.newBuilder() + .setModeValue(Signing.SignMode.SIGN_MODE_DIRECT_VALUE) + ) } private val channel = URI(chaincodeProperties.grpcUrl).let { uri -> - Logger.getLogger("io.netty").setLevel(Level.ALL) NettyChannelBuilder.forAddress(uri.host, uri.port) .also { if (uri.scheme == "grpcs") { @@ -70,11 +82,13 @@ class ProvenanceGrpcService( private val metadataQueryService = MetadataQueryGrpc.newBlockingStub(channel) private val bech32Address = accountProvider.bech32Address() - private val keyPair = accountProvider.getKeyPair() - private val signer = PbSigner.signerFor(keyPair) + private val p8eKeyPair = accountProvider.getKeyPair() + private val p8eSignerMeta = p8eKeyPair.toSignerMeta() + + fun accountInfo(): Auth.BaseAccount = accountInfo(bech32Address) - fun accountInfo(): Auth.BaseAccount = accountService.account(QueryOuterClass.QueryAccountRequest.newBuilder() - .setAddress(bech32Address) + fun accountInfo(address: String): Auth.BaseAccount = accountService.account(QueryOuterClass.QueryAccountRequest.newBuilder() + .setAddress(address) .build() ).run { account.unpack(Auth.BaseAccount::class.java) } @@ -84,7 +98,7 @@ class ProvenanceGrpcService( fun getTx(hash: String): TxResponse = txService.getTx(GetTxRequest.newBuilder().setHash(hash).build()).txResponse - fun signTx(body: TxBody, accountNumber: Long, sequenceNumber: Long, gasEstimate: GasEstimate = GasEstimate(0)): Tx { + fun signTx(body: TxBody, accountNumber: Long, sequenceNumber: Long, gasEstimate: GasEstimate = GasEstimate(0), signer: SignerMeta = p8eSignerMeta): Tx { val authInfo = AuthInfo.newBuilder() .setFee(Fee.newBuilder() .addAllAmount(listOf( @@ -98,14 +112,10 @@ class ProvenanceGrpcService( SignerInfo.newBuilder() .setPublicKey( Keys.PubKey.newBuilder() - .setKey(keyPair.getCompressedPublicKey().toByteString()) + .setKey(signer.compressedPublicKey.toByteString()) .build().toAny() ) - .setModeInfo( - ModeInfo.newBuilder().setSingle( - ModeInfo.Single.newBuilder() - .setModeValue(Signing.SignMode.SIGN_MODE_DIRECT_VALUE) - )) + .setModeInfo(DIRECT_SIGN_MODE) .setSequence(sequenceNumber) .build() )).build() @@ -117,7 +127,7 @@ class ProvenanceGrpcService( .setAccountNumber(accountNumber) .build() .toByteArray() - .let { signer(it) } + .let { signer.sign(it) } .map { it.signature.toByteString() } return Tx.newBuilder() @@ -127,16 +137,16 @@ class ProvenanceGrpcService( .build() } - fun estimateTx(body: TxBody, accountNumber: Long, sequenceNumber: Long): GasEstimate = - signTx(body, accountNumber, sequenceNumber).let { + fun estimateTx(body: TxBody, accountNumber: Long, sequenceNumber: Long, signer: SignerMeta = p8eSignerMeta): GasEstimate = + signTx(body, accountNumber, sequenceNumber, signer = signer).let { txService.simulate(SimulateRequest.newBuilder() .setTx(it) .build() ) }.let { GasEstimate(it.gasInfo.gasUsed) } - fun batchTx(body: TxBody, accountNumber: Long, sequenceNumber: Long, estimate: GasEstimate): BroadcastTxResponse = - signTx(body, accountNumber, sequenceNumber, estimate).run { + fun batchTx(body: TxBody, accountNumber: Long, sequenceNumber: Long, estimate: GasEstimate, signer: SignerMeta = p8eSignerMeta): BroadcastTxResponse = + signTx(body, accountNumber, sequenceNumber, estimate, signer).run { TxRaw.newBuilder() .setBodyBytes(body.toByteString()) .setAuthInfoBytes(authInfo.toByteString()) diff --git a/p8e-common/src/main/kotlin/io/p8e/crypto/PBSigner.kt b/p8e-common/src/main/kotlin/io/p8e/crypto/PBSigner.kt index dab34ef4..a47877fd 100644 --- a/p8e-common/src/main/kotlin/io/p8e/crypto/PBSigner.kt +++ b/p8e-common/src/main/kotlin/io/p8e/crypto/PBSigner.kt @@ -1,14 +1,20 @@ package io.provenance.engine.crypto import io.p8e.crypto.Hash +import io.p8e.crypto.Pen +import io.p8e.util.base64decode import io.provenance.pbc.clients.StdPubKey import io.provenance.pbc.clients.StdSignature +import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey +import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey import org.kethereum.crypto.getCompressedPublicKey import org.kethereum.crypto.impl.ec.EllipticCurveSigner import org.kethereum.model.ECKeyPair +import java.security.KeyPair +typealias SignerFn = (ByteArray) -> List object PbSigner { - fun signerFor(keyPair: ECKeyPair): (ByteArray) -> List = { bytes -> + fun signerFor(keyPair: ECKeyPair): SignerFn = { bytes -> bytes.let { Hash.sha256(it) }.let { @@ -20,4 +26,41 @@ object PbSigner { listOf(it) } } + + fun signerFor(keyPair: KeyPair): SignerFn = { bytes -> + bytes.let { + Hash.sha256(it) + }.let { + val privateKey = (keyPair.private as BCECPrivateKey).s + StdSignature( + pub_key = StdPubKey("tendermint/PubKeySecp256k1", (keyPair.public as BCECPublicKey).q.getEncoded(true)) , + signature = EllipticCurveSigner().sign(it, privateKey, true).encodeAsBTC() // todo: account for signature provider??? + ) + }.let { + listOf(it) + } + } } + +data class SignerMeta(val compressedPublicKey: ByteArray, val sign: SignerFn) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as SignerMeta + + if (!compressedPublicKey.contentEquals(other.compressedPublicKey)) return false + if (sign != other.sign) return false + + return true + } + + override fun hashCode(): Int { + var result = compressedPublicKey.contentHashCode() + result = 31 * result + sign.hashCode() + return result + } +} + +fun ECKeyPair.toSignerMeta() = SignerMeta(this.getCompressedPublicKey(), PbSigner.signerFor(this)) +fun KeyPair.toSignerMeta() = SignerMeta((public as BCECPublicKey).q.getEncoded(true), PbSigner.signerFor(this)) diff --git a/p8e-shared/build.gradle b/p8e-shared/build.gradle index a100de82..6ece4b3c 100644 --- a/p8e-shared/build.gradle +++ b/p8e-shared/build.gradle @@ -8,11 +8,13 @@ buildscript { dependencies { classpath "org.springframework.boot:spring-boot-gradle-plugin:$springboot_version" classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version" + classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.7' } } plugins { id 'java-library' + id 'com.google.protobuf' } apply plugin: 'kotlin-spring' @@ -27,14 +29,28 @@ dependencies { implementation project(":p8e-common") implementation project(":p8e-encryption") implementation project(":os-client") - implementation project(":p8e-proto-internal") + compile project(":p8e-proto-internal") implementation "com.auth0:java-jwt:$jwt_version" implementation group: 'org.bouncycastle', name: 'bcpkix-jdk15on', version: bouncy_castle + // Kethereum dependencies + implementation "com.github.komputing:kbip44:0.1" + implementation "com.github.komputing:kbase58:0.1" + implementation "com.github.komputing.khex:core:$khex_version" + implementation "com.github.komputing.khex:extensions:$khex_version" + implementation "com.github.komputing.kethereum:bip32:$kethereum_version" + implementation "com.github.komputing.kethereum:bip39:$kethereum_version" + implementation "com.github.komputing.kethereum:model:$kethereum_version" + implementation "com.github.komputing.kethereum:extensions_kotlin:$kethereum_version" + implementation "com.github.komputing.kethereum:crypto:$kethereum_version" + implementation "com.github.komputing.kethereum:crypto_api:$kethereum_version" + implementation "com.github.komputing.kethereum:crypto_impl_bouncycastle:$kethereum_version" + // GRPC implementation("io.grpc:grpc-protobuf:$grpc_version") + implementation("io.grpc:grpc-stub:$grpc_version") implementation "com.google.protobuf:protobuf-java-util:$protobuf_version" implementation("io.grpc:grpc-netty:$grpc_version") @@ -53,6 +69,7 @@ dependencies { implementation "org.jetbrains.exposed:exposed-core:$exposed_version" implementation "org.jetbrains.exposed:exposed-dao:$exposed_version" implementation "org.jetbrains.exposed:exposed-jdbc:$exposed_version" + implementation "org.jetbrains.exposed:exposed-jodatime:$exposed_version" implementation "com.zaxxer:HikariCP:$hikari_cp_version" // Spring @@ -61,3 +78,9 @@ dependencies { implementation "org.springframework.boot:spring-boot-starter-actuator:$springboot_version" implementation "org.springframework.boot:spring-boot-starter-validation:$springboot_version" } + +protobuf { + protoc { + artifact = "com.google.protobuf:protoc:$protobuf_version" + } +} From 9c1768ec44d6933b4ecbacd7dd52548b3ecaac8b Mon Sep 17 00:00:00 2001 From: Wyatt Date: Tue, 22 Jun 2021 15:11:19 -0700 Subject: [PATCH 03/15] Working on adding to job --- .../io/provenance/engine/config/AppConfig.kt | 11 +- .../engine/service/ChaincodeInvokeService.kt | 15 +- .../service/MemorializeChaincodeService.kt | 143 ++++++++++++++++++ .../p8e/shared/service/DataAccessService.kt | 19 +++ p8e-shared/src/main/proto/p8e/jobs.proto | 5 + 5 files changed, 179 insertions(+), 14 deletions(-) create mode 100644 p8e-api/src/main/kotlin/io/provenance/engine/service/MemorializeChaincodeService.kt create mode 100644 p8e-shared/src/main/kotlin/io/provenance/p8e/shared/service/DataAccessService.kt diff --git a/p8e-api/src/main/kotlin/io/provenance/engine/config/AppConfig.kt b/p8e-api/src/main/kotlin/io/provenance/engine/config/AppConfig.kt index 1c68575e..4f0bb6df 100644 --- a/p8e-api/src/main/kotlin/io/provenance/engine/config/AppConfig.kt +++ b/p8e-api/src/main/kotlin/io/provenance/engine/config/AppConfig.kt @@ -17,12 +17,7 @@ import io.provenance.engine.grpc.interceptors.JwtServerInterceptor import io.provenance.engine.grpc.interceptors.UnhandledExceptionInterceptor import io.provenance.engine.index.query.Operation import io.provenance.engine.index.query.OperationDeserializer -import io.provenance.engine.service.DataDogMetricCollector -import io.provenance.engine.service.JobHandlerService -import io.provenance.engine.service.JobHandlerServiceFactory -import io.provenance.engine.service.LogFileMetricCollector -import io.provenance.engine.service.MetricsService -import io.provenance.engine.service.OSLocatorChaincodeService +import io.provenance.engine.service.* import io.provenance.p8e.shared.util.KeyClaims import io.provenance.p8e.shared.util.TokenManager import io.provenance.p8e.shared.state.EnvelopeStateEngine @@ -217,9 +212,11 @@ class AppConfig : WebMvcConfigurer { } @Bean - fun jobHandlerServiceFactory(osLocatorChaincodeService: OSLocatorChaincodeService): JobHandlerServiceFactory = { payload -> + fun jobHandlerServiceFactory(osLocatorChaincodeService: OSLocatorChaincodeService, + memorializeChaincodeService: MemorializeChaincodeService): JobHandlerServiceFactory = { payload -> when (payload.jobCase) { Jobs.P8eJob.JobCase.ADDAFFILIATEOSLOCATOR -> osLocatorChaincodeService + Jobs.P8eJob.JobCase.MEMORIALIZEENVELOPE -> memorializeChaincodeService else -> throw IllegalArgumentException("No handler registered for job of type ${payload.jobCase.name}") } } diff --git a/p8e-api/src/main/kotlin/io/provenance/engine/service/ChaincodeInvokeService.kt b/p8e-api/src/main/kotlin/io/provenance/engine/service/ChaincodeInvokeService.kt index 882f6f02..a7bec177 100644 --- a/p8e-api/src/main/kotlin/io/provenance/engine/service/ChaincodeInvokeService.kt +++ b/p8e-api/src/main/kotlin/io/provenance/engine/service/ChaincodeInvokeService.kt @@ -181,15 +181,16 @@ class ChaincodeInvokeService( log.debug("Internal structures\nblockScopeIds: $blockScopeIds\npriorityFutureScopeToQueue: ${priorityScopeBacklog.entries.map { e -> "${e.key} => ${e.value.size}"}}") try { - val txBody = batch.flatMap { - it.attempts++ - //TODO this might break the error index matching - listOf(it.request, it.addDataAccessRequest).filterNotNull() - }.toTxBody() -// val txBody = batch.map { +// val txBody = batch.flatMap { // it.attempts++ -// it.request +// //TODO this might break the error index matching +// listOf(it.request, it.addDataAccessRequest).filterNotNull() // }.toTxBody() + // TODO send to job here instead + val txBody = batch.map { + it.attempts++ + it.request + }.toTxBody() // Send the transactions to the blockchain. val resp = batchTx(txBody) diff --git a/p8e-api/src/main/kotlin/io/provenance/engine/service/MemorializeChaincodeService.kt b/p8e-api/src/main/kotlin/io/provenance/engine/service/MemorializeChaincodeService.kt new file mode 100644 index 00000000..beb08fa6 --- /dev/null +++ b/p8e-api/src/main/kotlin/io/provenance/engine/service/MemorializeChaincodeService.kt @@ -0,0 +1,143 @@ +package io.provenance.engine.service + +import cosmos.auth.v1beta1.Auth +import cosmos.bank.v1beta1.Tx +import cosmos.base.abci.v1beta1.Abci +import cosmos.base.v1beta1.CoinOuterClass +import cosmos.tx.v1beta1.ServiceOuterClass +import io.p8e.crypto.Hash +import io.p8e.util.orThrow +import io.p8e.util.orThrowNotFound +import io.p8e.util.toHex +import io.p8e.util.toJavaPrivateKey +import io.p8e.util.toJavaPublicKey +import io.p8e.util.toPublicKey +import io.provenance.engine.config.ObjectStoreLocatorProperties +import io.provenance.engine.crypto.Bech32 +import io.provenance.engine.crypto.toBech32Data +import io.provenance.engine.crypto.toSignerMeta +import io.provenance.metadata.v1.MsgBindOSLocatorRequest +import io.provenance.metadata.v1.ObjectStoreLocator +import io.provenance.engine.config.ChaincodeProperties +import io.provenance.engine.crypto.Account +import io.provenance.p8e.shared.domain.AffiliateRecord +import io.provenance.p8e.shared.extension.logger +import io.provenance.p8e.shared.service.AffiliateService +import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey +import org.jetbrains.exposed.sql.transactions.transaction +import org.springframework.stereotype.Service +import p8e.Jobs +import java.lang.IllegalStateException +import java.security.KeyPair +import java.security.PublicKey + +@Service +class MemorializeChaincodeService( + private val chaincodeProperties: ChaincodeProperties, + private val p8eAccount: Account, + private val provenanceGrpcService: ProvenanceGrpcService, + private val objectStoreLocatorProperties: ObjectStoreLocatorProperties, + private val affiliateService: AffiliateService, + private val chaincodeInvokeService: ChaincodeInvokeService, +) : JobHandlerService { + private val log = logger() + + override fun handle(payload: Jobs.P8eJob) { + val job = payload.addDataAccess + val publicKey = job.publicKey.toPublicKey() + val affiliateAddress = publicKey.toBech32Address(chaincodeProperties.mainNet) + + logger().info("Handling os locator job for public key ${publicKey.toHex()}") + + val affiliate = transaction { affiliateService.get(publicKey) }.orThrowNotFound("Affiliate with public key ${publicKey.toHex()} not found") + val affiliateKeyPair = KeyPair(affiliate.publicKey.value.toJavaPublicKey(), affiliate.privateKey.toJavaPrivateKey()) + + // estimate amount of hash needed for locator request + val p8eAccountInfo = provenanceGrpcService.accountInfo(p8eAccount.bech32Address()) + val osLocatorEstimate = estimateLocatorRequestFee(p8eAccountInfo) + + // perform and wait for memorialize action to complete + waitForTx { + memorialize(affiliateAddress, osLocatorEstimate.fees) + } + + // perform and wait for add data access to complete + waitForTx { + recordOSLocator(affiliateKeyPair, affiliateAddress, osLocatorEstimate) + } + } + + fun estimateLocatorRequestFee(account: Auth.BaseAccount): GasEstimate = osLocatorMessage(account).let { + provenanceGrpcService.estimateTx(it.toTxBody(), account.accountNumber, account.sequence).also { + log.info("OS Locator estimated fees = ${it.fees}") + } + } + + fun transferHash(toAddress: String, amount: Long) = Tx.MsgSend.newBuilder() + .addAllAmount(listOf(CoinOuterClass.Coin.newBuilder() + .setAmount(amount.toString()) // todo: No clue how much to transfer for initial OS Locator message... + .setDenom("nhash") + .build() + )).setFromAddress(p8eAccount.bech32Address()) + .setToAddress(toAddress) + .build().let { + chaincodeInvokeService.batchTx(it.toTxBody()) + } + + fun recordOSLocator(affiliateKeyPair: KeyPair, affiliateAddress: String, gasEstimate: GasEstimate): ServiceOuterClass.BroadcastTxResponse { + val accountInfo = provenanceGrpcService.accountInfo(affiliateAddress) + val message = osLocatorMessage(accountInfo) + return provenanceGrpcService.batchTx(message.toTxBody(), accountInfo.accountNumber, accountInfo.sequence, gasEstimate, affiliateKeyPair.toSignerMeta()).also { + log.info("recordOSLocator response $it") + } + } + + fun osLocatorMessage(account: Auth.BaseAccount) = MsgBindOSLocatorRequest.newBuilder() + .setLocator(ObjectStoreLocator.newBuilder() + .setOwner(account.address) + .setLocatorUri(objectStoreLocatorProperties.url) + // todo: add in p8e owner address + ) + .build() + + fun waitForTx(block: () -> ServiceOuterClass.BroadcastTxResponse): Abci.TxResponse { + val txResponse = block() + val txHash = txResponse.txResponse.txhash + + if (txResponse.txResponse.code != 0) { + throw Exception("Error submitting transaction [code = ${txResponse.txResponse.code}, codespace = ${txResponse.txResponse.codespace}, raw_log = ${txResponse.txResponse.rawLog}]") + } + + val maxAttempts = 5 + log.info("Waiting for transaction to complete [hash = $txHash]") + for (i in 1 .. maxAttempts) { + Thread.sleep(2500) + val response = try { + provenanceGrpcService.getTx(txHash) + } catch (t: Throwable) { + log.info("Error fetching transaction [hash = $txHash, message = ${t.message}]") + continue + } + + when { + response.code == 0 -> { + log.info("Transaction complete [hash = $txHash]") + return response + } + response.code > 0 -> throw Exception("Transaction Failed with log ${response.rawLog}") + else -> continue // todo: what are the failure conditions, non-0 code... tx not found... under which conditions might it eventually succeed, not found needs a retry? + } + } + throw Exception("Failed to fetch transaction after $maxAttempts attempts [hash = $txHash]") + } + + // todo: this should really be somewhere more shared... but p8e-util where other key conversion extensions are doesn't have the Hash class... + private fun PublicKey.toBech32Address(mainNet: Boolean): String = + (this as BCECPublicKey).q.getEncoded(true) + .let { + Hash.sha256hash160(it) + }.let { + val prefix = if (mainNet) Bech32.PROVENANCE_MAINNET_ACCOUNT_PREFIX else Bech32.PROVENANCE_TESTNET_ACCOUNT_PREFIX + it.toBech32Data(prefix).address + } +} diff --git a/p8e-shared/src/main/kotlin/io/provenance/p8e/shared/service/DataAccessService.kt b/p8e-shared/src/main/kotlin/io/provenance/p8e/shared/service/DataAccessService.kt new file mode 100644 index 00000000..ca45bc87 --- /dev/null +++ b/p8e-shared/src/main/kotlin/io/provenance/p8e/shared/service/DataAccessService.kt @@ -0,0 +1,19 @@ +package io.provenance.p8e.shared.service + +import io.p8e.util.toPublicKeyProto +import io.provenance.p8e.shared.domain.JobRecord +import org.jetbrains.exposed.sql.transactions.transaction +import org.springframework.stereotype.Service +import p8e.Jobs +import java.security.PublicKey + +@Service +class DataAccessService { + fun addDataAccess(publicKey: PublicKey) = transaction { + JobRecord.create(Jobs.P8eJob.newBuilder() + .setAddDataAccess(Jobs.AddDataAccess.newBuilder() + .setPublicKey(publicKey.toPublicKeyProto()) + ) + .build()) + } +} diff --git a/p8e-shared/src/main/proto/p8e/jobs.proto b/p8e-shared/src/main/proto/p8e/jobs.proto index 060bfb86..b2bb7a26 100644 --- a/p8e-shared/src/main/proto/p8e/jobs.proto +++ b/p8e-shared/src/main/proto/p8e/jobs.proto @@ -7,9 +7,14 @@ import "p8e/public_key.proto"; message P8eJob { oneof job { AddAffiliateOSLocator addAffiliateOSLocator = 1; + AddDataAccess addDataAccess = 2; } } message AddAffiliateOSLocator { PublicKey publicKey = 1; } + +message AddDataAccess { + +} From 2819f84a2b753a4c120db89899318a71e373d298 Mon Sep 17 00:00:00 2001 From: Wyatt Date: Tue, 29 Jun 2021 14:26:27 -0700 Subject: [PATCH 04/15] making index handler more idiomatic kotlin and efficient --- .../kotlin/io/provenance/engine/event/IndexHandler.kt | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt b/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt index a5e88094..03216dd0 100644 --- a/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt +++ b/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt @@ -124,7 +124,7 @@ class IndexHandler( ), RequestOptions.DEFAULT ) // If the env is the invoker, create the data access message and put into a job. - if(envelope.isInvoker == true) { + if(envelope.isInvoker == true && envelope.data.input.affiliateSharesList.isNotEmpty()) { val envelopeDataAccess = envelope.data.input.affiliateSharesList.map { affiliateService.getAddress( it.toPublicKey(), chaincodeProperties.mainNet @@ -133,7 +133,7 @@ class IndexHandler( val existingScopeDataAccess = provenanceGrpcService.retrieveScopeData(envelope.data.input.scope.uuid.value).scope.scope.dataAccessList // Only perform job if data access will be updated if (envelopeDataAccess.any { it !in existingScopeDataAccess }) { - val scopeDataAccessRequest = p8e.Jobs.MsgAddScopeDataAccessRequest.newBuilder() + p8e.Jobs.MsgAddScopeDataAccessRequest.newBuilder() .addAllDataAccess(envelopeDataAccess) .addAllSigners(envelope.data.result.signaturesList.map { it.signer.signingPublicKey.toPublicKey().let { @@ -147,10 +147,7 @@ class IndexHandler( ) .setPublicKey(envelope.data.input.contract.invoker.encryptionPublicKey) .build().takeIf { envelope.data.input.affiliateSharesList.isNotEmpty() } - if (scopeDataAccessRequest != null) { - //Adding add data access to job - dataAccessService.addDataAccess(scopeDataAccessRequest) - } + ?.let { dataAccessService.addDataAccess(it) } } } } else { From 492f74793afb1e8267b1027e13cf84024bc89f6d Mon Sep 17 00:00:00 2001 From: Wyatt Date: Tue, 29 Jun 2021 14:42:21 -0700 Subject: [PATCH 05/15] Reverting class to os-locator --- .../engine/service/ChaincodeInvokeService.kt | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/p8e-api/src/main/kotlin/io/provenance/engine/service/ChaincodeInvokeService.kt b/p8e-api/src/main/kotlin/io/provenance/engine/service/ChaincodeInvokeService.kt index a31bddbd..7bb568bf 100644 --- a/p8e-api/src/main/kotlin/io/provenance/engine/service/ChaincodeInvokeService.kt +++ b/p8e-api/src/main/kotlin/io/provenance/engine/service/ChaincodeInvokeService.kt @@ -10,24 +10,21 @@ import io.p8e.proto.ContractSpecs.ContractSpec import io.p8e.proto.Contracts import io.p8e.util.* import io.provenance.engine.config.ChaincodeProperties -import io.provenance.engine.crypto.Account -import io.provenance.engine.crypto.asBech32PublicKey -import io.provenance.engine.crypto.toSignerMeta import io.provenance.p8e.shared.extension.logger import io.provenance.engine.domain.TransactionStatusRecord -import io.provenance.engine.util.PROV_METADATA_PREFIX_SCOPE_ADDR -import io.provenance.engine.util.toAddress import io.provenance.engine.util.toProv -import io.provenance.metadata.v1.* +import io.provenance.metadata.v1.Description +import io.provenance.metadata.v1.MsgP8eMemorializeContractRequest +import io.provenance.metadata.v1.MsgWriteP8eContractSpecRequest +import io.provenance.metadata.v1.MsgWriteScopeSpecificationRequest +import io.provenance.metadata.v1.ScopeSpecification +import io.provenance.engine.crypto.Account import io.provenance.p8e.shared.domain.ContractSpecificationRecord import io.provenance.p8e.shared.domain.ContractTxResult import io.provenance.p8e.shared.domain.ScopeSpecificationRecord -import io.provenance.p8e.shared.service.AffiliateService import io.provenance.pbc.clients.* import org.jetbrains.exposed.sql.transactions.transaction -import org.kethereum.crypto.toAddress import org.springframework.stereotype.Component -import java.security.KeyPair import java.time.OffsetDateTime import java.util.* import java.util.concurrent.CompletableFuture @@ -43,7 +40,7 @@ data class ContractRequestWrapper( val executionUuid: UUID, val request: MsgP8eMemorializeContractRequest, val future: CompletableFuture, - ) +) @Component class ChaincodeInvokeService( @@ -187,6 +184,7 @@ class ChaincodeInvokeService( it.attempts++ it.request }.toTxBody() + // Send the transactions to the blockchain. val resp = batchTx(txBody) @@ -220,6 +218,7 @@ class ChaincodeInvokeService( decrementSequenceNumber() log.warn("Unexpected chain execution error", t) val errorMessage = t.message ?: "Unexpected chain execution error" + val retryable = t.message?.contains("account sequence mismatch") == true val matchIndex = t.message?.matchIndex() @@ -230,8 +229,7 @@ class ChaincodeInvokeService( retryable -> { handleBatchRetry(errorMessage) } - - else -> { // fail the whole batch + else -> { // fail the whole batch batch.map { it.future.completeExceptionally(t) it.executionUuid @@ -346,6 +344,7 @@ class ChaincodeInvokeService( Contracts.ContractType.CHANGE_SCOPE, Contracts.ContractType.UNRECOGNIZED -> throw IllegalStateException("Unrecognized contract type of ${env.contract.typeValue} for envelope ${env.executionUuid.value}") } + val future = CompletableFuture() // TODO what to do when this offer fails? @@ -416,6 +415,7 @@ class ChaincodeInvokeService( val sequenceNumber = getAndIncrementSequenceNumber() val estimate = provenanceGrpc.estimateTx(body, accountNumber, sequenceNumber) + if (applyMultiplier && gasMultiplierDailyCount < chaincodeProperties.maxGasMultiplierPerDay) { log.info("setting gasMultiplier to ${chaincodeProperties.gasMultiplier} (current count = $gasMultiplierDailyCount)") estimate.setGasMultiplier(chaincodeProperties.gasMultiplier) @@ -425,8 +425,8 @@ class ChaincodeInvokeService( } else { log.info("skipping gasMultiplier due to daily limit") } - // TODO this is the hardcoded way to force the update through - provenanceGrpc.batchTx(body, accountNumber, sequenceNumber, estimate ) + + provenanceGrpc.batchTx(body, accountNumber, sequenceNumber, estimate) } /** From b68bb0b74feaedccbf4f402f9ee0f2e3c13638b9 Mon Sep 17 00:00:00 2001 From: Wyatt Date: Wed, 30 Jun 2021 09:55:03 -0700 Subject: [PATCH 06/15] Using more of a kotlin style in handling some functions --- .../provenance/engine/event/IndexHandler.kt | 56 +++++++++++-------- .../service/DataAccessChaincodeService.kt | 9 ++- 2 files changed, 39 insertions(+), 26 deletions(-) diff --git a/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt b/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt index 03216dd0..bb61800d 100644 --- a/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt +++ b/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt @@ -125,30 +125,7 @@ class IndexHandler( ) // If the env is the invoker, create the data access message and put into a job. if(envelope.isInvoker == true && envelope.data.input.affiliateSharesList.isNotEmpty()) { - val envelopeDataAccess = envelope.data.input.affiliateSharesList.map { - affiliateService.getAddress( - it.toPublicKey(), chaincodeProperties.mainNet - ) - } - val existingScopeDataAccess = provenanceGrpcService.retrieveScopeData(envelope.data.input.scope.uuid.value).scope.scope.dataAccessList - // Only perform job if data access will be updated - if (envelopeDataAccess.any { it !in existingScopeDataAccess }) { - p8e.Jobs.MsgAddScopeDataAccessRequest.newBuilder() - .addAllDataAccess(envelopeDataAccess) - .addAllSigners(envelope.data.result.signaturesList.map { - it.signer.signingPublicKey.toPublicKey().let { - affiliateService.getAddress(it, chaincodeProperties.mainNet) - } - }) - .setScopeId( - envelope.data.input.ref.scopeUuid.value.toUuidProv().toAddress( - PROV_METADATA_PREFIX_SCOPE_ADDR - ).toByteString() - ) - .setPublicKey(envelope.data.input.contract.invoker.encryptionPublicKey) - .build().takeIf { envelope.data.input.affiliateSharesList.isNotEmpty() } - ?.let { dataAccessService.addDataAccess(it) } - } + updateDataAccess(envelope) } } else { log.warn("Skipping ES indexing for stale scope") @@ -205,4 +182,35 @@ class IndexHandler( request.opType(OpType.INDEX) return request } + + private fun updateDataAccess(envelope: EnvelopeRecord) { + val envelopeDataAccess = envelope.data.input.affiliateSharesList.map { + affiliateSharePublicKey -> + affiliateService.getAddress( + affiliateSharePublicKey.toPublicKey(), chaincodeProperties.mainNet +// it.toPublicKey(), chaincodeProperties.mainNet + ) + } + val existingScopeDataAccess = provenanceGrpcService.retrieveScopeData(envelope.data.input.scope.uuid.value).scope.scope.dataAccessList + // Only perform job if data access will be updated + if (envelopeDataAccess.any { it !in existingScopeDataAccess }) { + p8e.Jobs.MsgAddScopeDataAccessRequest.newBuilder() + .addAllDataAccess(envelopeDataAccess) + .addAllSigners(envelope.data.result.signaturesList.map { + signature -> + signature.signer.signingPublicKey.toPublicKey().let { + signerPublicKey -> + affiliateService.getAddress(signerPublicKey, chaincodeProperties.mainNet) + } + }) + .setScopeId( + envelope.data.input.ref.scopeUuid.value.toUuidProv().toAddress( + PROV_METADATA_PREFIX_SCOPE_ADDR + ).toByteString() + ) + .setPublicKey(envelope.data.input.contract.invoker.encryptionPublicKey) + .build().takeIf { envelope.data.input.affiliateSharesList.isNotEmpty() } + ?.let { dataAccessService.addDataAccess(it) } + } + } } diff --git a/p8e-api/src/main/kotlin/io/provenance/engine/service/DataAccessChaincodeService.kt b/p8e-api/src/main/kotlin/io/provenance/engine/service/DataAccessChaincodeService.kt index f662be52..74e01319 100644 --- a/p8e-api/src/main/kotlin/io/provenance/engine/service/DataAccessChaincodeService.kt +++ b/p8e-api/src/main/kotlin/io/provenance/engine/service/DataAccessChaincodeService.kt @@ -21,6 +21,9 @@ import p8e.Jobs import java.security.KeyPair import java.security.PublicKey +// One to One Billion ratio from hash to nhash +private const val HASH_TO_NHASH = 1000000000L + @Service class DataAccessChaincodeService( private val chaincodeProperties: ChaincodeProperties, @@ -54,9 +57,11 @@ class DataAccessChaincodeService( ) // perform and wait for hash transfer to complete if account has less than 10 hash - if (provenanceGrpcService.getAccountCoins(affiliateAddress)[0].amount.toLong() / 1000000000 < 10) { + if ((provenanceGrpcService.getAccountCoins(affiliateAddress).find {it.denom == "nhash"}?.amount?.toLong() + ?: 0L) / HASH_TO_NHASH < 10 + ) { waitForTx { - transferHash(affiliateAccount.address, 10000000000) + transferHash(affiliateAccount.address, 100 * HASH_TO_NHASH) } } val resp = provenanceGrpcService.batchTx( From 0d44bbfa93be5b40916ea7042223c39288fef4e3 Mon Sep 17 00:00:00 2001 From: Wyatt Date: Wed, 30 Jun 2021 10:18:09 -0700 Subject: [PATCH 07/15] Removing commented line --- .../src/main/kotlin/io/provenance/engine/event/IndexHandler.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt b/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt index bb61800d..0e786209 100644 --- a/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt +++ b/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt @@ -188,7 +188,6 @@ class IndexHandler( affiliateSharePublicKey -> affiliateService.getAddress( affiliateSharePublicKey.toPublicKey(), chaincodeProperties.mainNet -// it.toPublicKey(), chaincodeProperties.mainNet ) } val existingScopeDataAccess = provenanceGrpcService.retrieveScopeData(envelope.data.input.scope.uuid.value).scope.scope.dataAccessList From 15bcc7ba82c44f072fd9a3b38ba14b01dce9b0ef Mon Sep 17 00:00:00 2001 From: Stephen Cirner Date: Fri, 2 Jul 2021 14:02:01 -0400 Subject: [PATCH 08/15] Add grpc endpoint for getting the current authenticated users shares. --- .../engine/grpc/v1/AffiliateGrpc.kt | 23 ++++++++++++++----- .../src/main/proto/p8e/affiliate.proto | 5 ++++ .../main/kotlin/io/p8e/client/EngineClient.kt | 6 +++++ .../p8e/shared/service/AffiliateService.kt | 6 ----- 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/p8e-api/src/main/kotlin/io/provenance/engine/grpc/v1/AffiliateGrpc.kt b/p8e-api/src/main/kotlin/io/provenance/engine/grpc/v1/AffiliateGrpc.kt index f6133751..6002eaf0 100644 --- a/p8e-api/src/main/kotlin/io/provenance/engine/grpc/v1/AffiliateGrpc.kt +++ b/p8e-api/src/main/kotlin/io/provenance/engine/grpc/v1/AffiliateGrpc.kt @@ -6,15 +6,16 @@ import io.p8e.grpc.complete import io.p8e.grpc.publicKey import io.p8e.proto.Affiliate import io.p8e.proto.Affiliate.AffiliateContractWhitelist +import io.p8e.proto.Affiliate.AffiliateSharesResponse import io.p8e.proto.AffiliateServiceGrpc.AffiliateServiceImplBase import io.p8e.util.computePublicKey import io.p8e.util.toHex import io.p8e.util.toPrivateKey +import io.p8e.util.toPublicKeyProto import io.provenance.p8e.shared.extension.logger import io.provenance.engine.grpc.interceptors.JwtServerInterceptor import io.provenance.engine.grpc.interceptors.UnhandledExceptionInterceptor import io.provenance.p8e.shared.service.AffiliateService -import io.provenance.engine.service.MailboxService import io.provenance.p8e.shared.util.P8eMDC import org.jetbrains.exposed.sql.transactions.transaction import org.lognet.springboot.grpc.GRpcService @@ -41,11 +42,6 @@ class AffiliateGrpc( log.info("Saving affiliate encryption key: ${ecPublicKey.toHex()}") - // TODO rethink this in decentralized system - // if (mailboxService.encryptionPublicKeyExists(ecPublicKey)) { - // throw IllegalArgumentException("Derived encryption public key [${ecPublicKey.toHex()}] is not allowed, please choose another.") - // } - transaction { affiliateService.save( signingKeyPair = KeyPair(publicKey, privateKey), @@ -55,6 +51,21 @@ class AffiliateGrpc( responseObserver.complete() } + override fun shares( + request: Empty, + responseObserver: StreamObserver + ) { + // TODO public key needs to be the authenticated public key + val sharePublicKeys = transaction { affiliateService.getShares(publicKey()).map { it.publicKey } } + + responseObserver.onNext( + AffiliateSharesResponse.newBuilder() + .addAllShares(sharePublicKeys.map { it.toPublicKeyProto() }) + .build() + ) + responseObserver.onCompleted() + } + override fun whitelistClass( request: AffiliateContractWhitelist, responseObserver: StreamObserver diff --git a/p8e-proto-internal/src/main/proto/p8e/affiliate.proto b/p8e-proto-internal/src/main/proto/p8e/affiliate.proto index 3a866367..fef1ced4 100644 --- a/p8e-proto-internal/src/main/proto/p8e/affiliate.proto +++ b/p8e-proto-internal/src/main/proto/p8e/affiliate.proto @@ -12,6 +12,7 @@ option java_outer_classname = "Affiliate"; service AffiliateService { rpc Register (AffiliateRegisterRequest) returns (google.protobuf.Empty) {}; + rpc Shares (google.protobuf.Empty) returns (AffiliateSharesResponse) {}; rpc WhitelistClass (AffiliateContractWhitelist) returns (google.protobuf.Empty) {}; } @@ -20,6 +21,10 @@ message AffiliateRegisterRequest { PrivateKey encryption_private_key = 2; } +message AffiliateSharesResponse { + repeated PublicKey shares = 1; +} + message AffiliateContractWhitelist { string classname = 1; google.protobuf.Timestamp start_time = 2; diff --git a/p8e-sdk/src/main/kotlin/io/p8e/client/EngineClient.kt b/p8e-sdk/src/main/kotlin/io/p8e/client/EngineClient.kt index 15798a77..78d2e3d0 100644 --- a/p8e-sdk/src/main/kotlin/io/p8e/client/EngineClient.kt +++ b/p8e-sdk/src/main/kotlin/io/p8e/client/EngineClient.kt @@ -29,6 +29,7 @@ import io.p8e.util.toPublicKeyProto import io.p8e.util.toOffsetDateTimeProv import io.p8e.util.toProtoTimestampProv import io.p8e.util.toProtoUuidProv +import io.p8e.util.toPublicKey import io.provenance.p8e.encryption.ecies.ECUtils import java.io.InputStream import java.lang.reflect.Method @@ -43,6 +44,8 @@ interface P8eClient { fun register(enc: Affiliate.AffiliateRegisterRequest) + fun shares(): List + fun whitelistClass(whitelist: AffiliateContractWhitelist) fun getContracts(groupUuid: UUID): EnvelopeCollection @@ -119,6 +122,9 @@ abstract class EventMonitorClient( override fun register(enc: Affiliate.AffiliateRegisterRequest) = signingAndEncryptionPublicKeysClient.register(enc) + override fun shares() = signingAndEncryptionPublicKeysClient.shares().sharesList + .map { it.toPublicKey() } + override fun whitelistClass(whitelist: AffiliateContractWhitelist) = signingAndEncryptionPublicKeysClient.whitelistClass(whitelist) override fun getContracts(groupUuid: UUID): EnvelopeCollection = envelopeClient.getAllByGroupUuid(groupUuid) diff --git a/p8e-shared/src/main/kotlin/io/provenance/p8e/shared/service/AffiliateService.kt b/p8e-shared/src/main/kotlin/io/provenance/p8e/shared/service/AffiliateService.kt index 8811dd1c..fe53a453 100644 --- a/p8e-shared/src/main/kotlin/io/provenance/p8e/shared/service/AffiliateService.kt +++ b/p8e-shared/src/main/kotlin/io/provenance/p8e/shared/service/AffiliateService.kt @@ -255,12 +255,6 @@ class AffiliateService( .whitelistData } - fun getSharePublicKeys(publicKeys: Collection): AffiliateSharePublicKeys = - AffiliateShareRecord.findByAffiliates(publicKeys) - .map { it.typedPublicKey() } - .toSet() - .let(::AffiliateSharePublicKeys) - fun getShares(affiliatePublicKey: PublicKey): List = AffiliateShareRecord.findByAffiliate(affiliatePublicKey).toList() fun addShare(affiliatePublicKey: PublicKey, publicKey: PublicKey) = AffiliateShareRecord.insert(affiliatePublicKey, publicKey) From 5eecb4b0974b7f6e4ae42723855624dc12f56943 Mon Sep 17 00:00:00 2001 From: Stephen Cirner Date: Fri, 2 Jul 2021 16:03:28 -0400 Subject: [PATCH 09/15] Cache shares in contract manager. --- .../provenance/engine/grpc/v1/ObjectGrpc.kt | 4 +- .../engine/service/EnvelopeService.kt | 57 +++++++++---------- .../SigningAndEncryptionPublicKeysClient.kt | 9 ++- .../kotlin/io/p8e/engine/ContractEngine.kt | 46 +++++++-------- .../src/main/kotlin/io/p8e/ContractManager.kt | 19 ++++++- .../src/main/kotlin/io/p8e/proxy/Contract.kt | 27 +++++---- 6 files changed, 93 insertions(+), 69 deletions(-) diff --git a/p8e-api/src/main/kotlin/io/provenance/engine/grpc/v1/ObjectGrpc.kt b/p8e-api/src/main/kotlin/io/provenance/engine/grpc/v1/ObjectGrpc.kt index 4173da90..0ea07bb1 100644 --- a/p8e-api/src/main/kotlin/io/provenance/engine/grpc/v1/ObjectGrpc.kt +++ b/p8e-api/src/main/kotlin/io/provenance/engine/grpc/v1/ObjectGrpc.kt @@ -45,10 +45,8 @@ class ObjectGrpc( val msg = request.message.toByteArray() val encryptionKeyPair = transaction { affiliateService.getEncryptionKeyPair(publicKey()) } val signingKeyPair = transaction { affiliateService.getSigningKeyPair(publicKey()) } - val affiliateShares = transaction { affiliateService.getSharePublicKeys(request.toAudience().plus(publicKey())) } - // Update the dime's audience list to use encryption public keys. - val audience = request.toAudience().plus(affiliateShares.value).map { + val audience = request.toAudience().map { transaction { try { affiliateService.getEncryptionKeyPair(it).public diff --git a/p8e-api/src/main/kotlin/io/provenance/engine/service/EnvelopeService.kt b/p8e-api/src/main/kotlin/io/provenance/engine/service/EnvelopeService.kt index 153becb7..14c0ceea 100644 --- a/p8e-api/src/main/kotlin/io/provenance/engine/service/EnvelopeService.kt +++ b/p8e-api/src/main/kotlin/io/provenance/engine/service/EnvelopeService.kt @@ -15,7 +15,6 @@ import io.p8e.proto.Events.P8eEvent.Event.ENVELOPE_MAILBOX_OUTBOUND import io.p8e.proto.PK import io.p8e.util.* import io.provenance.p8e.shared.domain.EnvelopeRecord -import io.provenance.p8e.shared.domain.EnvelopeTable import io.provenance.p8e.shared.domain.ScopeRecord import io.provenance.engine.extension.* import io.provenance.engine.grpc.v1.toEvent @@ -60,37 +59,35 @@ class EnvelopeService( val signingKeyPair = affiliateService.getSigningKeyPair(publicKey) val pen = Pen(signingKeyPair.private, signingKeyPair.public) - val affiliateShares = affiliateService.getShares(publicKey); // Update the envelope for invoker and recitals with correct signing and encryption keys. val envelope = env.toBuilder() - .addAllAffiliateShares(affiliateShares.map{ it.publicKey.toPublicKeyProto() }) - .apply { - if(env.contract.startTime == Timestamp.getDefaultInstance()) { - contractBuilder.startTime = OffsetDateTime.now().toProtoTimestampProv() - } - contractBuilder - .clearInvoker() - .setInvoker( - PK.SigningAndEncryptionPublicKeys.newBuilder() - .setEncryptionPublicKey(affiliateService.getEncryptionKeyPair(env.contract.invoker.encryptionPublicKey.toPublicKey()).public.toPublicKeyProto()) - .setSigningPublicKey(affiliateService.getSigningKeyPair(env.contract.invoker.signingPublicKey.toPublicKey()).public.toPublicKeyProto()) - .build() - ) - .clearRecitals() - .addAllRecitals( - env.contract.recitalsList.map { - it.toBuilder() - .setSignerRole(it.signerRole) - .setAddress(it.address) - .setSigner( - PK.SigningAndEncryptionPublicKeys.newBuilder() - .setSigningPublicKey(affiliateService.getSigningKeyPair(it.signer.signingPublicKey.toPublicKey()).public.toPublicKeyProto()) - .setEncryptionPublicKey(affiliateService.getEncryptionKeyPair(it.signer.encryptionPublicKey.toPublicKey()).public.toPublicKeyProto()) - .build() - ).build() - } - ) - }.build() + .apply { + if(env.contract.startTime == Timestamp.getDefaultInstance()) { + contractBuilder.startTime = OffsetDateTime.now().toProtoTimestampProv() + } + contractBuilder + .clearInvoker() + .setInvoker( + PK.SigningAndEncryptionPublicKeys.newBuilder() + .setEncryptionPublicKey(affiliateService.getEncryptionKeyPair(env.contract.invoker.encryptionPublicKey.toPublicKey()).public.toPublicKeyProto()) + .setSigningPublicKey(affiliateService.getSigningKeyPair(env.contract.invoker.signingPublicKey.toPublicKey()).public.toPublicKeyProto()) + .build() + ) + .clearRecitals() + .addAllRecitals( + env.contract.recitalsList.map { + it.toBuilder() + .setSignerRole(it.signerRole) + .setAddress(it.address) + .setSigner( + PK.SigningAndEncryptionPublicKeys.newBuilder() + .setSigningPublicKey(affiliateService.getSigningKeyPair(it.signer.signingPublicKey.toPublicKey()).public.toPublicKeyProto()) + .setEncryptionPublicKey(affiliateService.getEncryptionKeyPair(it.signer.encryptionPublicKey.toPublicKey()).public.toPublicKeyProto()) + .build() + ).build() + } + ) + }.build() val result = timed("EnvelopeService_contractEngine_handle") { ContractEngine(osClient, affiliateService).handle( diff --git a/p8e-common/src/main/kotlin/io/p8e/grpc/client/SigningAndEncryptionPublicKeysClient.kt b/p8e-common/src/main/kotlin/io/p8e/grpc/client/SigningAndEncryptionPublicKeysClient.kt index 27c9498a..06e1bb24 100644 --- a/p8e-common/src/main/kotlin/io/p8e/grpc/client/SigningAndEncryptionPublicKeysClient.kt +++ b/p8e-common/src/main/kotlin/io/p8e/grpc/client/SigningAndEncryptionPublicKeysClient.kt @@ -1,7 +1,9 @@ package io.p8e.grpc.client +import com.google.protobuf.Empty import io.grpc.ManagedChannel import io.p8e.proto.Affiliate +import io.p8e.proto.Affiliate.AffiliateSharesResponse import io.p8e.proto.AffiliateServiceGrpc import java.util.concurrent.TimeUnit @@ -20,10 +22,15 @@ class SigningAndEncryptionPublicKeysClient ( .register(request) } + fun shares() : AffiliateSharesResponse { + return client.withDeadlineAfter(deadlineMs, TimeUnit.MILLISECONDS) + .shares(Empty.getDefaultInstance()) + } + fun whitelistClass( whitelist: Affiliate.AffiliateContractWhitelist ) { client.withDeadlineAfter(deadlineMs, TimeUnit.MILLISECONDS) .whitelistClass(whitelist) } -} \ No newline at end of file +} diff --git a/p8e-engine/src/main/kotlin/io/p8e/engine/ContractEngine.kt b/p8e-engine/src/main/kotlin/io/p8e/engine/ContractEngine.kt index 70e58554..1b898e3e 100644 --- a/p8e-engine/src/main/kotlin/io/p8e/engine/ContractEngine.kt +++ b/p8e-engine/src/main/kotlin/io/p8e/engine/ContractEngine.kt @@ -72,7 +72,6 @@ class ContractEngine( keyPair, memoryClassLoader, pen, - affiliateService.getSharePublicKeys(listOf(keyPair.public)), scope, signingKeyPair, spec @@ -86,7 +85,6 @@ class ContractEngine( keyPair: KeyPair, memoryClassLoader: MemoryClassLoader, pen: Pen, - shares: AffiliateSharePublicKeys, scope: Scope?, signingKeyPair: KeyPair, spec: ContractSpec @@ -125,23 +123,24 @@ class ContractEngine( contract.validateAgainst(spec) when (contract.type!!) { - Contracts.ContractType.CHANGE_SCOPE -> { - if (scope != null && - envelope.status == Envelope.Status.CREATED && - contract.invoker.encryptionPublicKey == keyPair.public.toPublicKeyProto() - ) { - val audience = scope.partiesList.map { it.signer.signingPublicKey } - .plus(contract.recitalsList.map { it.signer.signingPublicKey }) - .map { it.toPublicKey() } - .let { it + affiliateService.getSharePublicKeys(it).value } - .toSet() - - log.debug("Change scope ownership - adding ${audience.map { it.toHex() }} [scope: ${scope.uuid.value}] [executionUuid: ${envelope.executionUuid.value}]") - - this.getScopeData(keyPair, definitionService, scope) - .threadedMap(executor) { definitionService.save(keyPair, it, signingKeyPair, audience) } - } else { } - } + // Contracts.ContractType.CHANGE_SCOPE -> { + // if (scope != null && + // envelope.status == Envelope.Status.CREATED && + // contract.invoker.encryptionPublicKey == keyPair.public.toPublicKeyProto() + // ) { + // val audience = scope.partiesList.map { it.signer.signingPublicKey } + // .plus(contract.recitalsList.map { it.signer.signingPublicKey }) + // .map { it.toPublicKey() } + // .let { it + affiliateService.getSharePublicKeys(it).value } + // .toSet() + + // log.debug("Change scope ownership - adding ${audience.map { it.toHex() }} [scope: ${scope.uuid.value}] [executionUuid: ${envelope.executionUuid.value}]") + + // this.getScopeData(keyPair, definitionService, scope) + // .threadedMap(executor) { definitionService.save(keyPair, it, signingKeyPair, audience) } + // } else { } + // } + Contracts.ContractType.CHANGE_SCOPE -> throw IllegalStateException("CHANGE_SCOPE contract type is not implemented") Contracts.ContractType.FACT_BASED, Contracts.ContractType.UNRECOGNIZED -> Unit } as Unit @@ -187,7 +186,7 @@ class ContractEngine( definitionService, prerequisite.fact.name, result, - contract.toAudience(scope, shares), + contract.toAudience(envelope, scope), signingKeyPair, keyPair, scope @@ -228,7 +227,7 @@ class ContractEngine( definitionService, function.fact.name, result, - contract.toAudience(scope, shares), + contract.toAudience(envelope, scope), signingKeyPair, keyPair, scope @@ -342,9 +341,8 @@ class ContractEngine( data class ResultSetter(val setter: () -> Unit) -fun Contract.toAudience(scope: Scope?, shares: AffiliateSharePublicKeys): Set = recitalsList +fun Contract.toAudience(envelope: Envelope, scope: Scope?): Set = recitalsList .filter { it.hasSigner() } - //.map { it.signer.encryptionPublicKeyPem } .map { it.signer.encryptionPublicKey } .plus( scope?.partiesList @@ -354,7 +352,7 @@ fun Contract.toAudience(scope: Scope?, shares: AffiliateSharePublicKeys): Set Collection.threadedMap(executor: ExecutorService, fn: (T) -> K): Collection = diff --git a/p8e-sdk/src/main/kotlin/io/p8e/ContractManager.kt b/p8e-sdk/src/main/kotlin/io/p8e/ContractManager.kt index 87421306..e03a3c0b 100644 --- a/p8e-sdk/src/main/kotlin/io/p8e/ContractManager.kt +++ b/p8e-sdk/src/main/kotlin/io/p8e/ContractManager.kt @@ -57,6 +57,7 @@ import java.security.KeyPair import java.security.PrivateKey import java.security.PublicKey import java.time.Duration +import java.time.Instant import java.time.OffsetDateTime import java.util.ServiceLoader import java.util.UUID @@ -90,7 +91,7 @@ class ContractManager( /** * Create a new ContractManager for a given Party */ - fun create(keyPair: KeyPair, url: String? = null, deadlineMs: Long = 60000): ContractManager { + fun create(keyPair: KeyPair, url: String? = null, deadlineMs: Long = 60_000): ContractManager { val apiUrl = url ?: System.getenv("API_URL") ?: "http://localhost:8080/engine" val uri = URI(apiUrl) val customTrustStore = System.getenv("TRUST_STORE_PATH")?.let(::File) @@ -154,6 +155,22 @@ class ContractManager( private val heartbeatExecutor = ThreadPoolFactory.newScheduledThreadPool(1, "heartbeat-%d") private val heartbeatManagerExecutor = ThreadPoolFactory.newScheduledThreadPool(1, "heartbeat-manager-%d") + private var shares: List? = null + private var sharesFetchTime = Instant.now() + + fun affiliateShares(): List = + if (shares == null) { + shares = client.shares() + sharesFetchTime = Instant.now() + shares!! + } else if (Instant.now().minusSeconds(10 * 60).isAfter(sharesFetchTime)) { + shares = client.shares() + sharesFetchTime = java.time.Instant.now() + shares!! + } else { + shares!! + } + init { val logger = LogFactory.getLog("org.apache.http.wire") when (logger) { diff --git a/p8e-sdk/src/main/kotlin/io/p8e/proxy/Contract.kt b/p8e-sdk/src/main/kotlin/io/p8e/proxy/Contract.kt index 2399e240..29813ddf 100644 --- a/p8e-sdk/src/main/kotlin/io/p8e/proxy/Contract.kt +++ b/p8e-sdk/src/main/kotlin/io/p8e/proxy/Contract.kt @@ -437,16 +437,13 @@ class Contract( this.stagedContract = populateContract() - val permissionUpdater = PermissionUpdater( - contractManager, - this.stagedContract, - this.stagedContract.toAudience(envelope.scope) - ) - - permissionUpdater.saveConstructorArguments() - // Build the envelope for this execution this.executionEnvelope = envelope.toBuilder() + .also { builder -> + if (builder.affiliateSharesCount == 0) { + builder.addAllAffiliateShares(contractManager.affiliateShares().map { it.toPublicKeyProto() }) + } + } .setExecutionUuid(this.stagedExecutionUuid) .setContract(this.stagedContract) .also { builder -> @@ -459,8 +456,17 @@ class Contract( .clearSignatures() .build() + val permissionUpdater = PermissionUpdater( + contractManager, + this.stagedContract, + this.stagedContract.toAudience(executionEnvelope, envelope.scope) + ) + + permissionUpdater.saveConstructorArguments() + // TODO This probably should be removed since we can't on the fly install specs. It would have // to get saved during bootstrap addSpec + // TODO get affiliate shares on the specs before save to object-store saveSpec(contractManager) executed.set(true) @@ -471,7 +477,7 @@ class Contract( } fun saveSpec(contractManager: ContractManager) { - contractManager.saveProto(spec, audience = stagedContract.toAudience(envelope.scope)) + contractManager.saveProto(spec, audience = stagedContract.toAudience(this.executionEnvelope, envelope.scope)) } fun isCompleted(): Boolean { @@ -761,12 +767,13 @@ class Contract( } } - private fun Contracts.Contract.toAudience(scope: Scope): Set { + private fun Contracts.Contract.toAudience(envelope: Envelope, scope: Scope): Set { return recitalsList.plus(scope.partiesList) .filter { it.hasSigner() } .map { it.signer.encryptionPublicKey.publicKeyBytes } .filterNot { it.isEmpty } .map { ECUtils.convertBytesToPublicKey(it.toByteArray()) } + .plus(envelope.affiliateSharesList.map { it.toPublicKey() }) .toSet() } From c9153feab32c3f5aec94123a5ced27b0b8665d9c Mon Sep 17 00:00:00 2001 From: Wyatt Date: Fri, 9 Jul 2021 13:56:38 -0700 Subject: [PATCH 10/15] Correct update to data access when it already exists --- .../kotlin/io/provenance/engine/event/IndexHandler.kt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt b/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt index 0e786209..bbe59fa5 100644 --- a/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt +++ b/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt @@ -185,14 +185,15 @@ class IndexHandler( private fun updateDataAccess(envelope: EnvelopeRecord) { val envelopeDataAccess = envelope.data.input.affiliateSharesList.map { - affiliateSharePublicKey -> + affiliateSharePublicKey -> affiliateService.getAddress( affiliateSharePublicKey.toPublicKey(), chaincodeProperties.mainNet ) + }.filter { + it !in provenanceGrpcService.retrieveScopeData(envelope.data.input.scope.uuid.value).scope.scope.dataAccessList } - val existingScopeDataAccess = provenanceGrpcService.retrieveScopeData(envelope.data.input.scope.uuid.value).scope.scope.dataAccessList // Only perform job if data access will be updated - if (envelopeDataAccess.any { it !in existingScopeDataAccess }) { + if (envelopeDataAccess.isNotEmpty()) { p8e.Jobs.MsgAddScopeDataAccessRequest.newBuilder() .addAllDataAccess(envelopeDataAccess) .addAllSigners(envelope.data.result.signaturesList.map { @@ -209,7 +210,8 @@ class IndexHandler( ) .setPublicKey(envelope.data.input.contract.invoker.encryptionPublicKey) .build().takeIf { envelope.data.input.affiliateSharesList.isNotEmpty() } - ?.let { dataAccessService.addDataAccess(it) } + ?.let { + dataAccessService.addDataAccess(it) } } } } From 72128c1a8c4e6e4717b733072887588acad3243e Mon Sep 17 00:00:00 2001 From: Wyatt Date: Fri, 9 Jul 2021 13:58:26 -0700 Subject: [PATCH 11/15] Undo indentation change --- .../src/main/kotlin/io/provenance/engine/event/IndexHandler.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt b/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt index bbe59fa5..a097d359 100644 --- a/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt +++ b/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt @@ -185,7 +185,7 @@ class IndexHandler( private fun updateDataAccess(envelope: EnvelopeRecord) { val envelopeDataAccess = envelope.data.input.affiliateSharesList.map { - affiliateSharePublicKey -> + affiliateSharePublicKey -> affiliateService.getAddress( affiliateSharePublicKey.toPublicKey(), chaincodeProperties.mainNet ) From 04261cac39f5afa9614d0d501ebb02ffefefee5f Mon Sep 17 00:00:00 2001 From: Wyatt Date: Wed, 14 Jul 2021 09:52:42 -0700 Subject: [PATCH 12/15] Throwing error message on multiparty contracts using envelope recitals --- .../provenance/engine/event/IndexHandler.kt | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt b/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt index a097d359..490429ff 100644 --- a/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt +++ b/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt @@ -124,7 +124,10 @@ class IndexHandler( ), RequestOptions.DEFAULT ) // If the env is the invoker, create the data access message and put into a job. - if(envelope.isInvoker == true && envelope.data.input.affiliateSharesList.isNotEmpty()) { + if (envelope.data.input.contract.recitalsCount > 1) { + throw IllegalStateException("Received ${indexScope.eventType} event from chain but multiparty contracts aren't currently supported for adding data access.") + } + else if(envelope.isInvoker == true && envelope.data.input.affiliateSharesList.isNotEmpty()) { updateDataAccess(envelope) } } else { @@ -192,17 +195,21 @@ class IndexHandler( }.filter { it !in provenanceGrpcService.retrieveScopeData(envelope.data.input.scope.uuid.value).scope.scope.dataAccessList } + val signers = envelope.data.result.signaturesList.map { + signature -> + signature.signer.signingPublicKey.toPublicKey().let { + signerPublicKey -> + affiliateService.getAddress(signerPublicKey, chaincodeProperties.mainNet) + } + } +// if (signers.size > 1) { +// throw +// } // Only perform job if data access will be updated if (envelopeDataAccess.isNotEmpty()) { p8e.Jobs.MsgAddScopeDataAccessRequest.newBuilder() .addAllDataAccess(envelopeDataAccess) - .addAllSigners(envelope.data.result.signaturesList.map { - signature -> - signature.signer.signingPublicKey.toPublicKey().let { - signerPublicKey -> - affiliateService.getAddress(signerPublicKey, chaincodeProperties.mainNet) - } - }) + .addAllSigners(signers) .setScopeId( envelope.data.input.ref.scopeUuid.value.toUuidProv().toAddress( PROV_METADATA_PREFIX_SCOPE_ADDR From dc8897bd19df80a9db7f92e7adb802a869e30d77 Mon Sep 17 00:00:00 2001 From: Wyatt Date: Wed, 14 Jul 2021 12:01:52 -0700 Subject: [PATCH 13/15] Removing unneeded check and variable --- .../io/provenance/engine/event/IndexHandler.kt | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt b/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt index 490429ff..3b8fcae7 100644 --- a/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt +++ b/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt @@ -195,21 +195,17 @@ class IndexHandler( }.filter { it !in provenanceGrpcService.retrieveScopeData(envelope.data.input.scope.uuid.value).scope.scope.dataAccessList } - val signers = envelope.data.result.signaturesList.map { - signature -> - signature.signer.signingPublicKey.toPublicKey().let { - signerPublicKey -> - affiliateService.getAddress(signerPublicKey, chaincodeProperties.mainNet) - } - } -// if (signers.size > 1) { -// throw -// } // Only perform job if data access will be updated if (envelopeDataAccess.isNotEmpty()) { p8e.Jobs.MsgAddScopeDataAccessRequest.newBuilder() .addAllDataAccess(envelopeDataAccess) - .addAllSigners(signers) + .addAllSigners(envelope.data.result.signaturesList.map { + signature -> + signature.signer.signingPublicKey.toPublicKey().let { + signerPublicKey -> + affiliateService.getAddress(signerPublicKey, chaincodeProperties.mainNet) + } + }) .setScopeId( envelope.data.input.ref.scopeUuid.value.toUuidProv().toAddress( PROV_METADATA_PREFIX_SCOPE_ADDR From d4eb9c9ca01a9c790bd9688a0d7abfef00c9bda0 Mon Sep 17 00:00:00 2001 From: Wyatt Date: Wed, 14 Jul 2021 14:19:05 -0700 Subject: [PATCH 14/15] Checking multiparty by checking owner list --- .../io/provenance/engine/event/IndexHandler.kt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt b/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt index 3b8fcae7..f58bcc51 100644 --- a/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt +++ b/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt @@ -124,10 +124,7 @@ class IndexHandler( ), RequestOptions.DEFAULT ) // If the env is the invoker, create the data access message and put into a job. - if (envelope.data.input.contract.recitalsCount > 1) { - throw IllegalStateException("Received ${indexScope.eventType} event from chain but multiparty contracts aren't currently supported for adding data access.") - } - else if(envelope.isInvoker == true && envelope.data.input.affiliateSharesList.isNotEmpty()) { + if(envelope.isInvoker == true && envelope.data.input.affiliateSharesList.isNotEmpty()) { updateDataAccess(envelope) } } else { @@ -187,16 +184,20 @@ class IndexHandler( } private fun updateDataAccess(envelope: EnvelopeRecord) { + val scopeData = provenanceGrpcService.retrieveScopeData(envelope.data.input.scope.uuid.value) val envelopeDataAccess = envelope.data.input.affiliateSharesList.map { affiliateSharePublicKey -> affiliateService.getAddress( affiliateSharePublicKey.toPublicKey(), chaincodeProperties.mainNet ) }.filter { - it !in provenanceGrpcService.retrieveScopeData(envelope.data.input.scope.uuid.value).scope.scope.dataAccessList + it !in scopeData.scope.scope.dataAccessList } // Only perform job if data access will be updated - if (envelopeDataAccess.isNotEmpty()) { + if (scopeData.scope.scope.ownersCount < 2) { + log.error("Multiparty contracts aren't currently supported for adding data access.") + } + else if (envelopeDataAccess.isNotEmpty()) { p8e.Jobs.MsgAddScopeDataAccessRequest.newBuilder() .addAllDataAccess(envelopeDataAccess) .addAllSigners(envelope.data.result.signaturesList.map { From 9c438c044f312f86b7015d394d674b0803cfee58 Mon Sep 17 00:00:00 2001 From: Wyatt Date: Wed, 14 Jul 2021 14:42:35 -0700 Subject: [PATCH 15/15] Correct ownerCount check --- .../src/main/kotlin/io/provenance/engine/event/IndexHandler.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt b/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt index f58bcc51..e82f1ab7 100644 --- a/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt +++ b/p8e-api/src/main/kotlin/io/provenance/engine/event/IndexHandler.kt @@ -194,7 +194,7 @@ class IndexHandler( it !in scopeData.scope.scope.dataAccessList } // Only perform job if data access will be updated - if (scopeData.scope.scope.ownersCount < 2) { + if (scopeData.scope.scope.ownersCount > 1) { log.error("Multiparty contracts aren't currently supported for adding data access.") } else if (envelopeDataAccess.isNotEmpty()) {