Skip to content

Commit

Permalink
Merge pull request #6 from provenance-io/p8e-contract-execution
Browse files Browse the repository at this point in the history
initial pass of contract work
  • Loading branch information
cworsnop-figure authored Apr 27, 2022
2 parents 071c1bd + 63ae091 commit 324be41
Show file tree
Hide file tree
Showing 40 changed files with 501 additions and 123 deletions.
3 changes: 2 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import net.swiftzer.semver.SemVer

buildscript {
Expand All @@ -24,6 +23,7 @@ subprojects {
}

repositories {
mavenLocal()
mavenCentral()
}

Expand Down Expand Up @@ -107,6 +107,7 @@ allprojects {
version = semVersion

repositories {
mavenLocal()
mavenCentral()
}
}
Expand Down
29 changes: 16 additions & 13 deletions buildSrc/src/main/kotlin/Dependencies.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,16 @@ object Versions {
const val KotlinFaker = "1.7.1"
const val SpringMockk = "3.0.1"
const val Swagger = "1.6.2"
const val AssetModel = "0.1.2"
const val AssetModel = "0.1.+"
const val P8eScope = "0.4.9"
const val ProvenancePbc = Master
const val ProvenanceProtobuf = Master
const val WalletPbClient = Develop
const val ProvenanceHdWallet = "0.1.15"
const val ProvenanceClient = "1.0.5"
const val ProvenanceClient = "1.1.1"
const val Unirest = "3.13.6"
const val KeyAccessLib = "0.2.+"
const val LoanPackage = "0.1.8"
const val Grpc = "1.45.0"
const val ProvenanceProto = "1.8.0"
const val Reflections = "0.9.10"
}

object Plugins { // please keep this sorted in sections
Expand Down Expand Up @@ -158,6 +159,7 @@ object Dependencies {
val CoreKcache = DependencySpec("io.provenance:core-kcache", Versions.ProvenanceCore)
val CoreKcacheSpring = DependencySpec("io.provenance:core-kcache-spring", Versions.ProvenanceCore)
val CoreKafkaAggregator = DependencySpec("io.provenance:core-kafka-aggregator", Versions.ProvenanceCore)
val ProtoKotlin = DependencySpec("io.provenance:proto-kotlin", Versions.ProvenanceProto)
val CoreKafkaAggregatorSpring =
DependencySpec(
"io.provenance:core-kafka-aggregator-spring",
Expand All @@ -167,25 +169,24 @@ object Dependencies {
"org.springframework.boot:spring-boot-starter-security"
)
)
val CoreLogging = DependencySpec("io.provenance:core-logging", Versions.ProvenanceCore)
val AssetModel = DependencySpec("io.provenance.model:metadata-asset-model", Versions.AssetModel)
val LoanPackage = DependencySpec("io.provenance.loan-package:contract", Versions.LoanPackage)

object Client {
val GrpcClientKotlin = DependencySpec("io.provenance.client:pb-grpc-client-kotlin", Versions.ProvenanceClient)
}

object HdWallet {
val HdWallet = DependencySpec("io.provenance.hdwallet:hdwallet", Versions.ProvenanceHdWallet)
val HdWalletBase58 = DependencySpec("io.provenance.hdwallet:hdwallet-base58", Versions.ProvenanceHdWallet)
val HdWalletBech32 = DependencySpec("io.provenance.hdwallet:hdwallet-bech32", Versions.ProvenanceHdWallet)
val HdWalletBip32 = DependencySpec("io.provenance.hdwallet:hdwallet-bip32", Versions.ProvenanceHdWallet)
val HdWalletBip39 = DependencySpec("io.provenance.hdwallet:hdwallet-bip39", Versions.ProvenanceHdWallet)
val HdWalletBip44 = DependencySpec("io.provenance.hdwallet:hdwallet-bip44", Versions.ProvenanceHdWallet)
val HdWalletEc = DependencySpec("io.provenance.hdwallet:hdwallet-ec", Versions.ProvenanceHdWallet)
val HdWalletSigner = DependencySpec("io.provenance.hdwallet:hdwallet-signer", Versions.ProvenanceHdWallet)
val HdWalletCommon = DependencySpec("io.provenance.hdwallet:hdwallet-common", Versions.ProvenanceHdWallet)
}
}

object Grpc {
val Protobuf = DependencySpec("io.grpc:grpc-protobuf", Versions.Grpc)
val Stub = DependencySpec("io.grpc:grpc-stub", Versions.Grpc)
}

val KotlinLogging = DependencySpec("io.github.microutils:kotlin-logging-jvm", Versions.KotlinLogging)

val Ktlint = DependencySpec("com.pinterest:ktlint", Versions.Ktlint)
Expand All @@ -201,6 +202,8 @@ object Dependencies {
object Swagger {
val Annotations = DependencySpec("io.swagger:swagger-annotations", Versions.Swagger)
}

val Reflections = DependencySpec("org.reflections:reflections", Versions.Reflections)
}

data class PluginSpec(
Expand Down
18 changes: 10 additions & 8 deletions dc.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ function up {
sleep 2
sh service/docker/vault/init-and-unseal.sh 'http://127.0.0.1:8200' 'kv2_originations'
sh service/docker/vault/add-secret.sh 'http://127.0.0.1:8200' 'kv2_originations' deadbeef-face-479b-860c-facefaceface \
0A4104F43FD6CD0DA184A666D444A4359EB9D38E06DB1EDC97B221DF90ED8189CFB8476EE2BF633DE0B4560F0D2BA284696C02ED31ADB32F09DEBE8D8906AA3E2BE71A \
0A410469BE2EC4699803FB7B49077F1CE0C88E2D2D41D23661EB1F420A72AA2B8DDF4325B349208B7F348C108B7C145FAEF41BB68FF338A75810522A89323E54BE91A6 \
0A4104DB326CF64C7466C2D63A2CD2C0401D1D6F8583C5F837C7510205DFB5CFA89D2115D0A3E0930D5FA01ACC664F1A182E1C0B43099D689B579849DEDDA1C63D0CF7 \
0A2009ABB890B396449706BB5998EEC6F6B0C795297B391A4BC2CF227B263555FF15 \
0A200B221F389D9B8E4964A8EB66F4EC07F83FCDD2E9DCE79B356FDDBE97767CF11C \
0A2100F5FE00731E3BC71F22CF054712A7C1F9A610848FC04BA81E6822D82D82C3562E \
"jealous bright oyster fluid guide talent crystal minor modify broken stove spoon pen thank action smart enemy chunk ladder soon focus recall elite pulp"
0A4104C51E49E4F0ABA2FD5B8CF99445D6D6C385164DBC8F35E7374CAC241D4155ADC48EF9B199F799DC865EC24AF54376CF5DD29A1287F1FD3410709A62F5DDE49349 \
0A4104C51E49E4F0ABA2FD5B8CF99445D6D6C385164DBC8F35E7374CAC241D4155ADC48EF9B199F799DC865EC24AF54376CF5DD29A1287F1FD3410709A62F5DDE49349 \
0A4104C51E49E4F0ABA2FD5B8CF99445D6D6C385164DBC8F35E7374CAC241D4155ADC48EF9B199F799DC865EC24AF54376CF5DD29A1287F1FD3410709A62F5DDE49349 \
0A201AD27E627DD0A6A5816D293C41DF801D3E7BA868EEC0F433FDA581D71E38016E \
0A201AD27E627DD0A6A5816D293C41DF801D3E7BA868EEC0F433FDA581D71E38016E \
0A201AD27E627DD0A6A5816D293C41DF801D3E7BA868EEC0F433FDA581D71E38016E \
"stable payment cliff fault abuse clinic bus belt film then forward world goose bring picnic rich special brush basic lamp window coral worry change"

sh service/docker/vault/add-secret.sh 'http://127.0.0.1:8200' 'kv2_originations' deadbeef-face-2222-860c-facefaceface \
0A4104F43FD6CD0DA184A666D444A4359EB9D38E06DB1EDC97B221DF90ED8189CFB8476EE2BF633DE0B4560F0D2BA284696C02ED31ADB32F09DEBE8D8906AA3E2BE71A \
Expand All @@ -52,9 +52,11 @@ function publish() {
source ./service/docker/bootstrap.env
export FULL_PATH=$(realpath $PATH_TO_CONTRACTS)

echo $SIGNING_PRIVATE_KEY
echo $ENCRYPTION_PRIVATE_KEY
if [[ -d "$FULL_PATH" ]]; then
pushd $PATH_TO_CONTRACTS > /dev/null
./gradlew p8eClean p8eBootstrap --info
./gradlew p8eClean p8eCheck p8eBootstrap --info && ./gradlew publishToMavenLocal -xsignMavenPublication --info
popd > /dev/null
else
echo "Invalid path. Provide a valid path to the contracts directory you wish to publish."
Expand Down
6 changes: 6 additions & 0 deletions service/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,18 @@ dependencies {
Dependencies.Provenance.HdWallet.HdWallet,
Dependencies.Provenance.HdWallet.HdWalletBip39,
Dependencies.Provenance.Client.GrpcClientKotlin,
Dependencies.Provenance.ProtoKotlin,
Dependencies.Provenance.LoanPackage,
Dependencies.Jackson.Databind,
Dependencies.Jackson.Datatype,
Dependencies.Jackson.Hubspot,
Dependencies.Jackson.KotlinModule,
Dependencies.Protobuf.JavaUtil,
Dependencies.Kong.Unirest,
Dependencies.Grpc.Protobuf,
Dependencies.Grpc.Stub,
Dependencies.Reflections,

).forEach { dep ->
dep.implementation(this)
}
Expand Down
4 changes: 2 additions & 2 deletions service/docker/bootstrap.env
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Export a mnemonic, private key, and address that can be used for provenance transactions and contract execution

export CHAINCODE_MNEMONIC_1="stable payment cliff fault abuse clinic bus belt film then forward world goose bring picnic rich special brush basic lamp window coral worry change"
export PRIVATE_KEY_1="0A2100EF4A9391903BFE252CB240DA6695BC5F680A74A8E16BEBA003833DFE9B18C147"
export ADDRESS_1="tp1hslffrztjp399d86a25fh2khg0c6je3achzhyj"
export PRIVATE_KEY_1="0A201AD27E627DD0A6A5816D293C41DF801D3E7BA868EEC0F433FDA581D71E38016E"
export ADDRESS_1="tp1s2c62ke0mmwhqxguf7e2pt6e98yq38m4atwhwl"

# p8e contract bootstrapping - signing and encryption keys are the same and match the private key above

Expand Down
2 changes: 1 addition & 1 deletion service/docker/dependencies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ services:

provenance:
image: provenanceio/provenance:v1.8.0-rc9
container_name: provenance-cee
container_name: provenance
command: bash -c "cp -rn /home/provenance_seed/* /home/provenance && /usr/bin/provenanced -t --home /home/provenance start"
networks:
- p8e-network
Expand Down
4 changes: 2 additions & 2 deletions service/docker/prov-init/config/genesis.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@
},
{
"@type": "/cosmos.auth.v1beta1.BaseAccount",
"address": "tp1hslffrztjp399d86a25fh2khg0c6je3achzhyj",
"address": "tp1s2c62ke0mmwhqxguf7e2pt6e98yq38m4atwhwl",
"pub_key": null,
"account_number": "0",
"sequence": "0"
Expand Down Expand Up @@ -184,7 +184,7 @@
]
},
{
"address": "tp1hslffrztjp399d86a25fh2khg0c6je3achzhyj",
"address": "tp1s2c62ke0mmwhqxguf7e2pt6e98yq38m4atwhwl",
"coins": [
{
"denom": "nhash",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.provenance.onboarding.domain.cee

import com.google.protobuf.Message

interface ContractParser {
fun parseInput(input: Any, type: Class<*>): Message
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.provenance.onboarding.domain.cee

import com.google.protobuf.Message
import cosmos.base.abci.v1beta1.Abci
import io.provenance.onboarding.frameworks.provenance.SingleTx
import io.provenance.scope.contract.spec.P8eContract
import io.provenance.scope.sdk.Client
import io.provenance.scope.sdk.Session
import java.util.UUID

interface ContractService {
fun getContract(contractName: String): Class<out P8eContract>
fun <T : P8eContract> setupContract(client: Client, contractClass: Class<T>, records: Map<String, Message>, scopeUuid: UUID, sessionUuid: UUID? = null): Session
fun executeContract(client: Client, session: Session, executeTransaction: (SingleTx) -> Abci.TxResponse): Result<Abci.TxResponse>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.provenance.onboarding.domain.cee

import com.google.protobuf.Message

interface InputParser {
val type: Class<*>
fun parse(input: Any, type: Class<*>): Message
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.provenance.onboarding.domain.provenance
package io.provenance.onboarding.domain.objectStore

import io.provenance.onboarding.domain.usecase.objectStore.model.StoreAssetResponse
import io.provenance.scope.encryption.proto.Encryption
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package io.provenance.onboarding.domain.provenance

import cosmos.base.abci.v1beta1.Abci
import io.provenance.client.grpc.Signer
import io.provenance.hdwallet.wallet.Account
import io.provenance.onboarding.domain.usecase.common.model.ProvenanceConfig
import io.provenance.onboarding.domain.usecase.common.model.TxBody
import io.provenance.onboarding.domain.usecase.provenance.tx.model.OnboardAssetResponse
import java.util.UUID
import io.provenance.onboarding.domain.usecase.common.model.TxResponse
import io.provenance.onboarding.frameworks.provenance.ProvenanceTx
import io.provenance.scope.sdk.Session

interface Provenance {
fun onboard(chainId: String, nodeEndpoint: String, account: Account, storeTxBody: TxBody): OnboardAssetResponse
fun writeSpecifications(chainId: String, nodeEndpoint: String, account: Account, scopeId: UUID, contractSpecId: UUID, scopeSpecId: UUID, type: String)
fun onboard(chainId: String, nodeEndpoint: String, account: Account, storeTxBody: TxBody): TxResponse
fun executeTransaction(config: ProvenanceConfig, session: Session, tx: ProvenanceTx, signer: Signer): Abci.TxResponse
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package io.provenance.onboarding.domain.usecase.cee

import com.google.protobuf.Message
import io.provenance.core.KeyType
import io.provenance.onboarding.domain.cee.ContractParser
import io.provenance.onboarding.domain.cee.ContractService
import io.provenance.onboarding.domain.provenance.Provenance
import io.provenance.onboarding.domain.usecase.AbstractUseCase
import io.provenance.onboarding.domain.usecase.cee.model.ExecuteContractRequest
import io.provenance.onboarding.domain.usecase.common.model.TxResponse
import io.provenance.onboarding.domain.usecase.common.originator.GetOriginator
import io.provenance.onboarding.domain.usecase.provenance.account.GetAccount
import io.provenance.onboarding.frameworks.provenance.utility.ProvenanceUtils
import io.provenance.scope.contract.annotations.Input
import io.provenance.scope.encryption.model.DirectKeyRef
import io.provenance.scope.encryption.util.toJavaPrivateKey
import io.provenance.scope.encryption.util.toJavaPublicKey
import io.provenance.scope.sdk.Affiliate
import io.provenance.scope.sdk.Client
import io.provenance.scope.sdk.ClientConfig
import io.provenance.scope.sdk.SharedClient
import java.net.URI
import io.provenance.scope.contract.spec.P8eContract
import java.security.KeyPair
import java.util.concurrent.TimeUnit
import kotlin.reflect.full.functions
import mu.KotlinLogging
import org.springframework.stereotype.Component

private val log = KotlinLogging.logger { }

@Component
class ExecuteContract(
private val getOriginator: GetOriginator,
private val contractService: ContractService,
private val provenanceService: Provenance,
private val getAccount: GetAccount,
private val contractParser: ContractParser,
) : AbstractUseCase<ExecuteContractRequest, TxResponse>() {

override suspend fun execute(args: ExecuteContractRequest): TxResponse {
val utils = ProvenanceUtils()
val account = getAccount.execute(args.config.account)
val originator = getOriginator.execute(args.config.account.originatorUuid)
val affiliate = Affiliate(
signingKeyRef = DirectKeyRef(KeyPair(originator.keys[KeyType.SIGNING_PUBLIC_KEY].toString().toJavaPublicKey(), originator.keys[KeyType.SIGNING_PRIVATE_KEY].toString().toJavaPrivateKey())),
encryptionKeyRef = DirectKeyRef(KeyPair(originator.keys[KeyType.SIGNING_PUBLIC_KEY].toString().toJavaPublicKey(), originator.keys[KeyType.SIGNING_PRIVATE_KEY].toString().toJavaPrivateKey())),
args.config.account.partyType,
)

val sharedClient = SharedClient(
ClientConfig(
mainNet = !args.config.account.isTestNet,
cacheJarSizeInBytes = 4L * 1024 * 1024, // ~ 4 MB,
cacheRecordSizeInBytes = 0L,
cacheSpecSizeInBytes = 0L,
disableContractLogs = !args.config.account.isTestNet,
osConcurrencySize = 6,
osGrpcUrl = URI(args.config.client.objectStoreUrl),
osChannelCustomizeFn = { channelBuilder ->
channelBuilder
.idleTimeout(1, TimeUnit.MINUTES)
.keepAliveTime(10, TimeUnit.SECONDS)
.keepAliveTimeout(10, TimeUnit.SECONDS)
}
)
)

val contract = contractService.getContract(args.config.contract.contractName)
val records = getRecords(args.records, contract)

val client = Client(sharedClient, affiliate)
val session = contractService.setupContract(client, contract, records, args.config.contract.scopeUuid, args.config.contract.sessionUuid)
val signer = utils.getSigner(account)

contractService.executeContract(client, session) { tx ->
provenanceService.executeTransaction(args.config.provenanceConfig, session, tx, signer)
}.fold(
onSuccess = { result ->
log.info("[L: ${session.scopeUuid}, S: ${session.sessionUuid}] ${contract.simpleName} is pending. The tx hash is ${result.txhash}.")
return TxResponse(result.txhash, result.gasWanted.toString(), result.gasUsed.toString(), result.height.toString())
},
onFailure = { throwable ->
log.error("[L: ${session.scopeUuid}, S: ${session.sessionUuid}] ${contract.simpleName} has failed execution. An error occurred.", throwable)
throw throwable
}
)
}

@Suppress("TooGenericExceptionCaught")
private fun getRecords(records: Map<String, Any>, contract: Class<out P8eContract>): Map<String, Message> {
val contractRecords = mutableMapOf<String, Message>()

try {
contract.kotlin.functions.forEach { func ->
func.parameters.forEach { param ->
(param.annotations.firstOrNull { it is Input } as? Input)?.let { input ->
val parameterClass = Class.forName(param.type.toString())
val recordToParse = records.getOrDefault(input.name, null)
?: throw IllegalStateException("Contract required input record with name ${input.name} but none was found!")
val record = contractParser.parseInput(recordToParse, parameterClass)
contractRecords[input.name] = record
}
}
}
} catch (ex: Exception) {
log.error("Failed to get inputs for contract ${contract.simpleName}")
throw ex
}

return contractRecords
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.provenance.onboarding.domain.usecase.cee.model

data class ClientConfig(
val objectStoreUrl: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.provenance.onboarding.domain.usecase.cee.model

import java.util.UUID

data class ContractConfig(
val contractName: String,
val scopeUuid: UUID,
val sessionUuid: UUID?,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.provenance.onboarding.domain.usecase.cee.model

import io.provenance.onboarding.domain.usecase.common.model.AccountInfo
import io.provenance.onboarding.domain.usecase.common.model.ProvenanceConfig

data class ExecuteContractConfig(
val contract: ContractConfig,
val client: ClientConfig,
val account: AccountInfo,
val provenanceConfig: ProvenanceConfig,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package io.provenance.onboarding.domain.usecase.cee.model

data class ExecuteContractRequest(
val config: ExecuteContractConfig,
val records: Map<String, Any>
)
Loading

0 comments on commit 324be41

Please sign in to comment.