Skip to content

Commit

Permalink
Merge pull request #14 from hyperschwartz/hyperschwartz/swap-to-bette…
Browse files Browse the repository at this point in the history
…r-invoice-processing-with-correct-asset-type

New Asset Formatting
  • Loading branch information
hyperschwartz authored Apr 27, 2022
2 parents b45ee6f + e2ddd14 commit d69c623
Show file tree
Hide file tree
Showing 14 changed files with 48 additions and 107 deletions.
5 changes: 5 additions & 0 deletions buildSrc/src/main/kotlin/Dependencies.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import org.gradle.plugin.use.PluginDependenciesSpec

object Versions {
const val Arrow = "1.0.1"
const val AssetModel = "0.1.2"
const val BouncyCastle = "1.70"
const val Exposed = "0.37.3"
const val Feign = "10.1.0"
Expand Down Expand Up @@ -143,6 +144,10 @@ object Dependencies {
val Core = DependencySpec("io.arrow-kt:arrow-core", Versions.Arrow)
}

object Asset {
val AssetModel = DependencySpec("io.provenance.model:metadata-asset-model", Versions.AssetModel)
}

val BouncyCastle = DependencySpec("org.bouncycastle:bcprov-jdk15on", Versions.BouncyCastle)
}

Expand Down
32 changes: 0 additions & 32 deletions proto/src/main/proto/asset_protos.proto

This file was deleted.

1 change: 1 addition & 0 deletions server/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ dependencies {
implementation(project(":proto"))
listOf(
Dependencies.Arrow.Core,
Dependencies.Asset.AssetModel,
Dependencies.BouncyCastle,
Dependencies.Database.Exposed,
Dependencies.Database.ExposedDao,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package io.provenance.invoice.testhelpers.testcontainers

import com.fasterxml.jackson.module.kotlin.readValue
import io.provenance.invoice.AssetProtos.Asset
import mu.KLogging
import org.mockserver.client.MockServerClient
import org.mockserver.model.HttpRequest.request
Expand All @@ -12,6 +11,7 @@ import org.testcontainers.utility.DockerImageName
import io.provenance.invoice.config.app.ConfigurationUtil.DEFAULT_OBJECT_MAPPER
import io.provenance.invoice.config.web.AppHeaders
import io.provenance.invoice.services.mock.AssetOnboardingMocker
import tech.figure.asset.v1beta1.Asset

class MockAssetOnboardingServiceContainer : TestContainerTemplate<MockServerContainer> {
private companion object : KLogging() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import cosmos.tx.v1beta1.TxOuterClass.TxBody
import feign.Headers
import feign.Param
import feign.RequestLine
import io.provenance.invoice.AssetProtos.Asset
import io.provenance.invoice.config.web.AppHeaders
import io.provenance.invoice.util.extension.checkNotNullI
import io.provenance.invoice.util.extension.deriveDefaultInstanceI
Expand All @@ -16,6 +15,7 @@ import io.provenance.invoice.util.extension.typedUnpackI
import io.provenance.metadata.v1.MsgWriteRecordRequest
import io.provenance.metadata.v1.MsgWriteScopeRequest
import io.provenance.metadata.v1.MsgWriteSessionRequest
import tech.figure.asset.v1beta1.Asset

@Headers("Content-Type: application/json")
interface OnboardingApiClient {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package io.provenance.invoice.services

import io.provenance.invoice.AssetProtos.Asset
import io.provenance.invoice.clients.OnboardingApiClient
import io.provenance.invoice.config.app.Qualifiers
import io.provenance.metadata.v1.MsgWriteRecordRequest
Expand All @@ -10,6 +9,7 @@ import mu.KLogging
import org.springframework.stereotype.Service
import io.provenance.invoice.util.provenance.ProvenanceAccountDetail
import org.springframework.beans.factory.annotation.Qualifier
import tech.figure.asset.v1beta1.Asset

@Service
class AssetOnboardingService(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package io.provenance.invoice.services

import arrow.core.Either
import arrow.core.getOrHandle
import io.provenance.invoice.AssetProtos.Asset
import io.provenance.invoice.config.provenance.ObjectStore
import io.provenance.invoice.factory.InvoiceCalcFactory
import io.provenance.invoice.repository.FailedEventRepository
Expand All @@ -13,12 +12,13 @@ import io.provenance.invoice.util.enums.InvoiceStatus
import io.provenance.invoice.util.eventstream.external.StreamEvent
import io.provenance.invoice.util.extension.attributeValueI
import io.provenance.invoice.util.extension.attributeValueOrNullI
import io.provenance.invoice.util.extension.unpackInvoiceI
import io.provenance.invoice.util.extension.toInvoiceI
import io.provenance.invoice.util.provenance.PayableContractKey
import io.provenance.invoice.util.validation.InvoiceValidator
import io.provenance.scope.sdk.extensions.resultHash
import mu.KLogging
import org.springframework.stereotype.Service
import tech.figure.asset.v1beta1.Asset
import java.time.OffsetDateTime
import java.util.UUID
import java.util.concurrent.TimeUnit
Expand Down Expand Up @@ -102,7 +102,7 @@ class EventHandlerService(
.getDecryptedPayload(objectStore.oracleAccountDetail.keyRef)
.use { signatureStream ->
val messageBytes = signatureStream.readAllBytes()
val targetInvoice = Asset.parseFrom(messageBytes).unpackInvoiceI()
val targetInvoice = Asset.parseFrom(messageBytes).toInvoiceI()
Either.catch {
InvoiceValidator.validateInvoiceForApproval(
invoiceDto = invoiceDto,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import io.provenance.invoice.util.extension.toAssetI
import io.provenance.invoice.util.extension.toProtoAnyI
import io.provenance.invoice.util.extension.toUuidI
import io.provenance.invoice.util.provenance.ProvenanceAccountDetail
import io.provenance.invoice.util.validation.InvoiceValidator
import io.provenance.invoice.util.validation.ValidatedInvoice
import org.springframework.beans.factory.annotation.Qualifier
import java.math.BigDecimal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import com.google.common.io.BaseEncoding
import cosmos.tx.v1beta1.TxOuterClass.TxBody
import io.provenance.invoice.AssetProtos.Asset
import io.provenance.invoice.clients.OnboardingResponse
import io.provenance.invoice.util.extension.toJsonProvenanceI
import io.provenance.invoice.util.extension.toProtoAnyI
Expand All @@ -28,6 +27,7 @@ import io.provenance.scope.encryption.ecies.ProvenanceKeyGenerator
import io.provenance.scope.objectstore.client.SIGNATURE_PUBLIC_KEY_FIELD_NAME
import io.provenance.scope.util.MetadataAddress
import io.provenance.scope.util.toByteString
import tech.figure.asset.v1beta1.Asset
import java.io.ByteArrayInputStream
import java.security.PublicKey
import java.util.Base64
Expand Down Expand Up @@ -64,7 +64,7 @@ object AssetOnboardingMocker {
): OnboardingResponse {
val decodedPublicKey = ECUtils.convertBytesToPublicKey(BaseEncoding.base64().decode(publicKey))
val hash = hashAsset(asset, decodedPublicKey)
val txBody = buildTxBody(asset.id.toUuidI(), hash, address)
val txBody = buildTxBody(asset.id.value.toUuidI(), hash, address)
return OnboardingResponse(
json = ObjectMapper().readValue(txBody.toJsonProvenanceI()),
base64 = txBody.messagesList.map { it.toByteArray().toBase64String() },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package io.provenance.invoice.services.mock

import io.provenance.invoice.AssetProtos.Asset
import io.provenance.invoice.clients.OnboardingApiClient
import io.provenance.invoice.clients.OnboardingResponse
import mu.KLogging
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.stereotype.Service
import tech.figure.asset.v1beta1.Asset

@Service
@ConditionalOnProperty(name = ["simulated.asset_onboarding_api"], havingValue = "true")
Expand All @@ -19,7 +19,7 @@ class MockOnboardingApiClient : OnboardingApiClient {
override fun onboardPayable(
address: String,
publicKey: String,
asset: Asset
asset: Asset,
): OnboardingResponse {
logger.error("Using simulated asset onboarding api client!")
return AssetOnboardingMocker.mockAssetResponse(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,52 +1,28 @@
package io.provenance.invoice.util.extension

import com.google.protobuf.DescriptorProtos.EnumValueOptions
import com.google.protobuf.GeneratedMessage.GeneratedExtension
import com.google.protobuf.MessageOrBuilder
import com.google.protobuf.ProtocolMessageEnum
import io.provenance.invoice.AssetProtos
import io.provenance.invoice.AssetProtos.Asset
import io.provenance.invoice.AssetProtos.AssetOrBuilder
import io.provenance.invoice.AssetProtos.AssetType
import io.provenance.invoice.AssetProtosBuilders
import io.provenance.invoice.InvoiceProtos.Invoice
import io.provenance.invoice.InvoiceProtos.InvoiceOrBuilder
import io.provenance.invoice.UtilProtos
import io.provenance.invoice.util.randomProtoUuidI
import tech.figure.asset.v1beta1.Asset
import tech.figure.asset.v1beta1.AssetOrBuilder
import java.math.BigDecimal

fun AssetType.provenanceNameI(): String = this.getExtensionValue(AssetProtos.provenanceName)
private const val ASSET_TYPE = "payable"
private const val INVOICE_KV_NAME = "invoice"

fun AssetType.assetKvNameI(): String = this.getExtensionValue(AssetProtos.assetKvName)

fun InvoiceOrBuilder.toAssetI(): Asset = this.toAssetI(
assetType = AssetType.NFT,
assetDescription = "Invoice [${this.invoiceUuid.value}]",
idProvider = { invoiceUuid },
)
fun InvoiceOrBuilder.toAssetI(): Asset = Asset.newBuilder().also { assetBuilder ->
assetBuilder.id = tech.figure.util.v1beta1.UUID.newBuilder().setValue(this.invoiceUuid.value).build()
assetBuilder.type = ASSET_TYPE
assetBuilder.description = "Invoice [${this.invoiceUuid.value}]"
assetBuilder.putKv(INVOICE_KV_NAME, this.toProtoAnyI())
}.build()

fun InvoiceOrBuilder.totalAmountI(): BigDecimal = lineItemsList.sumOf { it.quantity.toBigDecimal() * it.price.toBigDecimalOrZeroI() }

fun AssetOrBuilder.unpackInvoiceI(): Invoice = this
.checkI({ it.type == AssetType.NFT.name }) { "Cannot unpack invoice from asset. Expected invoice to be properly typed as [${AssetType.NFT.name}] but type was [$type]" }
.kvMap[AssetType.NFT.assetKvNameI()]
.checkNotNullI { "Expected the NFT to be serialized into the KV map of the asset under its KV name of [${AssetType.NFT.assetKvNameI()}]" }
.unpack(Invoice::class.java)

private fun <T: MessageOrBuilder> T.toAssetI(
assetType: AssetType,
assetDescription: String = assetType.provenanceNameI(),
idProvider: (T) -> UtilProtos.UUID = { randomProtoUuidI() },
): Asset = this.let { message ->
AssetProtosBuilders.Asset {
id = idProvider.invoke(message)
type = assetType.name
description = assetDescription
putKv(assetType.assetKvNameI(), message.toProtoAnyI())
}
}

private fun <T: ProtocolMessageEnum, U: Any> T.getExtensionValue(extension: GeneratedExtension<EnumValueOptions, U>): U =
this.valueDescriptor.options.getExtension(extension)


fun AssetOrBuilder.toInvoiceI(): Invoice = this
.checkI(
predicate = { asset -> asset.kvCount == 1 },
lazyMessage = { "An asset containing an invoice should only contain a single kv value, but found keys: ${this.kvMap.keys}" },
)
.kvMap[INVOICE_KV_NAME]
?.unpack(Invoice::class.java)
?: throw IllegalStateException("No key of type [$INVOICE_KV_NAME] could be found in asset kv map. Found keys: ${this.kvMap.keys}")
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package io.provenance.invoice.util.provenance

import io.provenance.invoice.AssetProtos.Asset
import io.provenance.invoice.AssetProtos.AssetType
import io.provenance.invoice.util.extension.tryOrNullI
import io.provenance.scope.util.Bech32
import java.security.MessageDigest
Expand All @@ -11,11 +9,6 @@ object ProvenanceUtil {
private const val MARKER_ENCODING_PREFIX: String = "marker/"
private const val MARKER_ENCODING_TRUNCATE_LENGTH: Int = 20

fun getInvoiceDenominationForAsset(asset: Asset): String {
check(asset.type == AssetType.NFT.name) { "Provided asset with id [${asset.id.value}] is not of expected type [${AssetType.NFT.name}]" }
return "$INVOICE_DENOM_PREFIX-${asset.id.value}"
}

/**
* Takes a source denomination for a marker and an account address from the target blockchain environment and
* computes a marker address.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class TestAssetOnboardingMocker {
@Test
fun testAssetOnboardingMockerResponses() {
val asset = MockInvoice.defaultProto().toAssetI()
val assetUuid = asset.id.toUuidI()
val assetUuid = asset.id.value.toUuidI()
val response = assertSucceeds("A valid public key and address should produce output") {
AssetOnboardingMocker.mockAssetResponse(
asset = asset,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,40 +1,39 @@
package io.provenance.invoice.util.extension

import helper.assertSucceeds
import io.provenance.invoice.AssetProtos.AssetType
import io.provenance.invoice.AssetProtosBuilders
import io.provenance.invoice.util.mock.MockInvoice
import io.provenance.invoice.util.randomProtoUuidI
import org.junit.jupiter.api.Test
import tech.figure.asset.v1beta1.Asset
import tech.figure.util.v1beta1.UUID
import kotlin.test.assertEquals
import kotlin.test.assertTrue

class AssetExtensionsTest {
@Test
fun testUnpackInvoiceFromAsset() {
fun testAssetToInvoice() {
val invoice = MockInvoice.defaultProto()
val asset = AssetProtosBuilders.Asset {
id = randomProtoUuidI()
type = AssetType.NFT.name
description = "Doesn't matter"
putKv(AssetType.NFT.assetKvNameI(), invoice.toProtoAnyI())
}
val asset = Asset.newBuilder().also { assetBuilder ->
assetBuilder.id = UUID.newBuilder().setValue(invoice.invoiceUuid.value).build()
assetBuilder.type = "payable"
assetBuilder.description = "Invoice [${invoice.invoiceUuid.value}]"
assetBuilder.putKv("invoice", invoice.toProtoAnyI())
}.build()
val unpackedInvoice = assertSucceeds("Expected the invoice kv to successfully unpack to an Invoice proto") {
asset.unpackInvoiceI()
asset.toInvoiceI()
}
assertEquals(expected = invoice, actual = unpackedInvoice, "The asset's packed invoice should properly deserialize into the original")
}

@Test
fun testPackInvoiceAsAsset() {
fun testInvoiceToAsset() {
val invoice = MockInvoice.defaultProto()
val asset = invoice.toAssetI()
assertTrue(actual = asset.id.isSet(), message = "Expected the newly-created asset to get an id")
assertEquals(expected = asset.type, actual = AssetType.NFT.name, message = "Expected the type to equate to the name of the NFT asset type enum")
assertEquals(expected = asset.type, actual = "payable", message = "Expected the type to equate to the payable type")
assertTrue(actual = asset.description.isNotBlank(), message = "Expected the description to be established")
assertEquals(expected = 1, actual = asset.kvCount, "Expected only one KV to be set")
val unpackedInvoice = assertSucceeds("Expected the invoice kv to successfully unpack to an Invoice proto") {
asset.unpackInvoiceI()
asset.toInvoiceI()
}
assertEquals(expected = invoice, actual = unpackedInvoice, "The asset's packed invoice should properly deserialize into the original")
}
Expand Down

0 comments on commit d69c623

Please sign in to comment.