Skip to content

Commit

Permalink
Chains config refactoring (#311)
Browse files Browse the repository at this point in the history
Generate code from chains config
  • Loading branch information
Termina1 authored Sep 29, 2023
1 parent a2c5743 commit ef6af89
Show file tree
Hide file tree
Showing 80 changed files with 1,527 additions and 1,321 deletions.
7 changes: 3 additions & 4 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,10 @@ jobs:
with:
distribution: 'zulu'
java-version: 20

- name: Upload to Docker
- name: Setup gradle
uses: gradle/gradle-build-action@v2
with:
arguments: jib -Pdocker=drpcorg
- name: Upload to Docker
run: make jib
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
Expand Down
8 changes: 3 additions & 5 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,12 @@ jobs:

- name: Install System Libs
run: sudo apt-get install -y openssl libapr1

- name: Check
- name: Setup gradle
uses: gradle/gradle-build-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
arguments: check
env:
CI: true
- name: Check
run: make test

- name: Upload Coverage Report
uses: codecov/codecov-action@v1
Expand Down
18 changes: 18 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
all: build-foundation build-main

build-foundation:
cd foundation && ../gradlew build publishToMavenLocal

build-main:
./gradlew build

test: build-foundation
./gradlew check


jib: build-foundation
./gradlew jib -Pdocker=drpcorg

clean:
./gradlew clean;
cd foundation && ../gradlew clean
14 changes: 13 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ plugins {
id 'idea'
id 'application'
id 'jacoco'
id 'chainsconfig.codegen'

alias(libs.plugins.kotlin)
alias(libs.plugins.jib)
Expand Down Expand Up @@ -119,6 +120,7 @@ dependencies {
implementation(variantOf(libs.netty.tcnative.boringssl) { classifier("osx-aarch_64") })
implementation(variantOf(libs.netty.tcnative.boringssl) { classifier("linux-x86_64") })
implementation(variantOf(libs.netty.tcnative.boringssl) { classifier("osx-x86_64") })
implementation 'dshackle:foundation:1.0.0'
}

compileKotlin {
Expand Down Expand Up @@ -224,7 +226,9 @@ protobuf {
sourceSets {
main {
resources.srcDirs += project.buildDir.absolutePath + "/generated/version"

kotlin {
srcDir project.buildDir.absolutePath + "/generated/kotlin"
}
proto {
srcDir 'emerald-grpc/proto'
}
Expand Down Expand Up @@ -318,3 +322,11 @@ static def getVersion() {

return result.replaceAll(/^v/, '')
}

ktlint {
filter {
exclude { element -> element.file.path.contains("generated/") }
}
}

compileKotlin.dependsOn chainscodegen
14 changes: 14 additions & 0 deletions buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
plugins {
`kotlin-dsl`
}

repositories {
mavenLocal()
mavenCentral()
}

dependencies {
implementation("org.yaml:snakeyaml:1.24")
implementation("dshackle:foundation:1.0.0")
implementation("com.squareup:kotlinpoet:1.14.2")
}
118 changes: 118 additions & 0 deletions buildSrc/src/main/kotlin/chainsconfig.codegen.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import com.squareup.kotlinpoet.*
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import io.emeraldpay.dshackle.config.ChainsConfig
import io.emeraldpay.dshackle.config.ChainsConfigReader
import io.emeraldpay.dshackle.foundation.ChainOptionsReader

open class CodeGen(private val config: ChainsConfig) {
companion object {
fun generateFromChains(path: File) {
val chainConfigReader = ChainsConfigReader(ChainOptionsReader())
val config = chainConfigReader.read(null)
CodeGen(config).generateChainsFile().writeTo(path)
}
}

private fun addEnumProperties(builder: TypeSpec.Builder): TypeSpec.Builder {
builder.addEnumConstant(
"UNSPECIFIED",
TypeSpec.anonymousClassBuilder()
.addSuperclassConstructorParameter("%L, %S, %S, %S, %L, %L", 0, "UNSPECIFIED", "Unknown", "0x0", 0, "emptyList()")
.build(),
)
for (chain in config) {
builder.addEnumConstant(
chain.blockchain.uppercase().replace('-', '_') + "__" + chain.id.uppercase().replace('-', '_'),
TypeSpec.anonymousClassBuilder()
.addSuperclassConstructorParameter(
"%L, %S, %S, %S, %L, %L",
chain.grpcId,
chain.code,
chain.blockchain.replaceFirstChar { it.uppercase() } + " " + chain.id.replaceFirstChar { it.uppercase() },
chain.chainId,
chain.netVersion,
"listOf(" + chain.shortNames.map { "\"${it}\"" }.joinToString() + ")",
)
.build(),
)
}
return builder
}

fun generateChainsFile(): FileSpec {
val byIdFun = FunSpec.builder("byId")
.addParameter("id", Int::class)
.returns(ClassName("", "Chain"))
.beginControlFlow("for (chain in values())")
.beginControlFlow("if (chain.id == id)")
.addStatement("return chain")
.endControlFlow()
.endControlFlow()
.addStatement("return UNSPECIFIED")
.build()

val chainType = addEnumProperties(
TypeSpec.enumBuilder("Chain")
.addType(TypeSpec.companionObjectBuilder().addFunction(byIdFun).build())
.primaryConstructor(
FunSpec.constructorBuilder()
.addParameter("id", Int::class)
.addParameter("chainCode", String::class)
.addParameter("chainName", String::class)
.addParameter("chainId", String::class)
.addParameter("netVersion", Long::class)
.addParameter("shortNames", List::class.asClassName().parameterizedBy(String::class.asClassName()))
.build(),
)
.addProperty(
PropertySpec.builder("id", Int::class)
.initializer("id")
.build(),
)
.addProperty(
PropertySpec.builder("chainCode", String::class)
.initializer("chainCode")
.build(),
)
.addProperty(
PropertySpec.builder("netVersion", Long::class)
.initializer("netVersion")
.build(),
)
.addProperty(
PropertySpec.builder("chainId", String::class)
.initializer("chainId")
.build(),
)
.addProperty(
PropertySpec.builder("chainName", String::class)
.initializer("chainName")
.build(),
)
.addProperty(
PropertySpec.builder("shortNames", List::class.asClassName().parameterizedBy(String::class.asClassName()))
.initializer("shortNames")
.build(),
),
).build()
return FileSpec.builder("io.emeraldpay.dshackle", "Chain")
.addType(chainType)
.build()
}
}

open class ChainsCodeGenTask : DefaultTask() {
init {
group = "custom"
description = "Generate chains config"
}

@TaskAction
fun chainscodegenClass() {
val output = project.layout.buildDirectory.dir("generated/kotlin").get().asFile
output.mkdirs()
CodeGen.generateFromChains(output)
}
}

tasks.register<ChainsCodeGenTask>("chainscodegen")
32 changes: 32 additions & 0 deletions foundation/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.9.10'
id 'maven-publish'
}

repositories {
mavenLocal()
mavenCentral()
}

group = 'dshackle'

dependencies {
implementation 'org.yaml:snakeyaml:1.24'
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.1'
}

test {
useJUnitPlatform()
}
version '1.0.0'

publishing {
publications {
mavenJava(MavenPublication) {
from components.java
}
}
repositories {
mavenLocal()
}
}
Empty file added foundation/settings.gradle
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package io.emeraldpay.dshackle.foundation

import java.time.Duration

class ChainOptions {
data class Options(
val disableUpstreamValidation: Boolean,
val disableValidation: Boolean,
val validationInterval: Int,
val timeout: Duration,
val providesBalance: Boolean?,
val validatePeers: Boolean,
val minPeers: Int,
val validateSyncing: Boolean,
val validateCallLimit: Boolean,
val validateChain: Boolean,
)

open class DefaultOptions : PartialOptions() {
var chains: List<String>? = null
var options: PartialOptions? = null
}

open class PartialOptions {
companion object {
@JvmStatic
fun getDefaults(): PartialOptions {
val options = PartialOptions()
options.minPeers = 1
return options
}
}

var disableValidation: Boolean? = null
var disableUpstreamValidation: Boolean? = null
var validationInterval: Int? = null
set(value) {
require(value == null || value > 0) {
"validation-interval must be a positive number: $value"
}
field = value
}
var timeout: Duration? = null
var providesBalance: Boolean? = null
var validatePeers: Boolean? = null
var validateCalllimit: Boolean? = null
var minPeers: Int? = null
set(value) {
require(value == null || value >= 0) {
"min-peers must be a positive number: $value"
}
field = value
}
var validateSyncing: Boolean? = null
var validateChain: Boolean? = null

fun merge(overwrites: PartialOptions?): PartialOptions {
if (overwrites == null) {
return this
}
val copy = PartialOptions()
copy.validatePeers = overwrites.validatePeers ?: this.validatePeers
copy.minPeers = overwrites.minPeers ?: this.minPeers
copy.disableValidation = overwrites.disableValidation ?: this.disableValidation
copy.validationInterval = overwrites.validationInterval ?: this.validationInterval
copy.providesBalance = overwrites.providesBalance ?: this.providesBalance
copy.validateSyncing = overwrites.validateSyncing ?: this.validateSyncing
copy.validateCalllimit = overwrites.validateCalllimit ?: this.validateCalllimit
copy.timeout = overwrites.timeout ?: this.timeout
copy.validateChain = overwrites.validateChain ?: this.validateChain
copy.disableUpstreamValidation =
overwrites.disableUpstreamValidation ?: this.disableUpstreamValidation
return copy
}

fun buildOptions(): Options =
Options(
this.disableUpstreamValidation ?: false,
this.disableValidation ?: false,
this.validationInterval ?: 30,
this.timeout ?: Duration.ofSeconds(60),
this.providesBalance,
this.validatePeers ?: true,
this.minPeers ?: 1,
this.validateSyncing ?: true,
this.validateCalllimit ?: true,
this.validateChain ?: true,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package io.emeraldpay.dshackle.foundation

import org.yaml.snakeyaml.nodes.MappingNode
import java.time.Duration

class ChainOptionsReader : YamlConfigReader<ChainOptions.PartialOptions>() {
override fun read(upNode: MappingNode?): ChainOptions.PartialOptions? {
return if (hasAny(upNode, "options")) {
return getMapping(upNode, "options")?.let { values ->
readOptions(values)
}
} else {
null
}
}

fun readOptions(values: MappingNode): ChainOptions.PartialOptions {
val options = ChainOptions.PartialOptions()
getValueAsBool(values, "validate-peers")?.let {
options.validatePeers = it
}
getValueAsBool(values, "validate-syncing")?.let {
options.validateSyncing = it
}
getValueAsBool(values, "validate-call-limit")?.let {
options.validateCalllimit = it
}
getValueAsBool(values, "validate-chain")?.let {
options.validateChain = it
}
getValueAsInt(values, "min-peers")?.let {
options.minPeers = it
}
getValueAsInt(values, "timeout")?.let {
options.timeout = Duration.ofSeconds(it.toLong())
}
getValueAsBool(values, "disable-validation")?.let {
options.disableValidation = it
}
getValueAsInt(values, "validation-interval")?.let {
options.validationInterval = it
}
getValueAsBool(values, "balance")?.let {
options.providesBalance = it
}
return options
}
}
Loading

0 comments on commit ef6af89

Please sign in to comment.