Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -874,16 +874,16 @@ object SpliceConfig {
s"Pruning retention period ${conf.participantPruningSchedule.map(_.retention)} must be bigger than the deduplication duration ${conf.deduplicationDuration}"
),
)
_ <- Either.cond(
{
val traffic = conf.domains.global.buyExtraTraffic
traffic.targetThroughput.value <= 0 || traffic.minTopupInterval.duration >= conf.automation.pollingInterval.duration
},
(),
ConfigValidationFailed(
s"topup interval ${conf.domains.global.buyExtraTraffic.minTopupInterval} must not be smaller than the polling interval ${conf.automation.pollingInterval}"
),
)
// Every synchronizer we top up, not just global. topupTargets already filters out
// zero-throughput entries.
_ <- conf.domains.topupTargets
.find(_._2.minTopupInterval.duration < conf.automation.pollingInterval.duration)
.toLeft(())
.leftMap { case (alias, topup) =>
ConfigValidationFailed(
s"topup interval ${topup.minTopupInterval} must not be smaller than the polling interval ${conf.automation.pollingInterval} on synchronizer ${alias.unwrap}"
)
}

_ <- Either.cond(
!conf.disableSvValidatorBftSequencerConnection || conf.svValidator,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import org.lfdecentralizedtrust.splice.wallet.config.{
AppRewardBeneficiaryConfig,
RewardSharingConfig,
}
import com.digitalasset.canton.config.CantonRequireTypes.InstanceName
import com.digitalasset.canton.config.NonNegativeFiniteDuration
import com.digitalasset.canton.topology.PartyId

Expand All @@ -31,6 +32,21 @@ class SpliceConfigTest extends AsyncWordSpec with BaseTest {
"topup interval 1 second must not be smaller than the polling interval 30 seconds"
)
}
"Validator config is rejected when an extra synchronizer topup interval < pollingInterval" in {
val overwrite = ConfigFactory.parseString(
"""
|canton.validator-apps.aliceValidator.domains.extra = [{
| alias = "dedicated"
| url = "http://localhost:5108"
| topup { target-throughput = 500000, min-topup-interval = 1s }
|}]
""".stripMargin
)
val buggyConfig = CantonConfig.mergeConfigs(config, Seq(overwrite))
SpliceConfig.loadAndValidate(buggyConfig).left.value.toString should include(
"topup interval 1 second must not be smaller than the polling interval 30 seconds on synchronizer dedicated"
)
}
"disableSvValidatorBftSequencerConnection" should {
"be rejected if svValidator is not true" in {
val overwrite = ConfigFactory.parseString(
Expand Down Expand Up @@ -86,6 +102,70 @@ class SpliceConfigTest extends AsyncWordSpec with BaseTest {
}
}

"domains.extra topup" should {
"default to a zero target, leaving existing entries unchanged" in {
val overwrite = ConfigFactory.parseString(
"""
|canton.validator-apps.aliceValidator.domains.extra = [
| { alias = "dedicated-1", url = "http://dedicated-1.example.com" }
|]
""".stripMargin
)
val extended = CantonConfig.mergeConfigs(config, Seq(overwrite))
val validator =
SpliceConfig
.loadAndValidate(extended)
.value
.validatorApps(InstanceName.tryCreate("aliceValidator"))
val extra = validator.domains.extra.loneElement
extra.alias.unwrap shouldBe "dedicated-1"
extra.topup.targetThroughput.value shouldBe BigDecimal(0)
validator.domains.topupTargets shouldBe empty
}

"be parsed when set, and surface through topupTargets" in {
val overwrite = ConfigFactory.parseString(
"""
|canton.validator-apps.aliceValidator.domains.extra = [
| { alias = "dedicated-1", url = "http://dedicated-1.example.com",
| topup { target-throughput = 5000, min-topup-interval = 1m } }
|]
""".stripMargin
)
val extended = CantonConfig.mergeConfigs(config, Seq(overwrite))
val validator =
SpliceConfig
.loadAndValidate(extended)
.value
.validatorApps(InstanceName.tryCreate("aliceValidator"))
validator.domains.extra.loneElement.topup.targetThroughput.value shouldBe BigDecimal(5000)
// the global target is 0 in the base topology, so only the dedicated synchronizer appears
validator.domains.topupTargets.map(_._1.unwrap) shouldBe Seq("dedicated-1")
}

"list the global synchronizer first when both have non-zero targets" in {
val overwrite = ConfigFactory.parseString(
"""
|canton.validator-apps.aliceValidator.domains.global.buy-extra-traffic.target-throughput = 20000
|canton.validator-apps.aliceValidator.domains.extra = [
| { alias = "dedicated-1", url = "http://dedicated-1.example.com",
| topup { target-throughput = 5000 } }
|]
""".stripMargin
)
val extended = CantonConfig.mergeConfigs(config, Seq(overwrite))
val validator =
SpliceConfig
.loadAndValidate(extended)
.value
.validatorApps(InstanceName.tryCreate("aliceValidator"))
validator.domains.topupTargets.map(_._2.targetThroughput.value) shouldBe Seq(
BigDecimal(20000),
BigDecimal(5000),
)
}
}

// Shared helper for RewardSharingConfig tests
private def mkSharingCfg(percentages: BigDecimal*): RewardSharingConfig.BuiltIn =
RewardSharingConfig.BuiltIn(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,22 @@ case class ValidatorTrustedSynchronizerConfig(
case class ValidatorExtraSynchronizerConfig(
alias: SynchronizerAlias,
url: String,
topup: BuyExtraTrafficConfig = BuyExtraTrafficConfig(),
)

case class ValidatorSynchronizerConfig(
global: ValidatorDecentralizedSynchronizerConfig,
extra: Seq[ValidatorExtraSynchronizerConfig] = Seq(),
)
) {

/** Synchronizers with a non-zero top-up target, global first.
*
* For the top-up trigger fan-out to build a `ValidatorTopupConfig` per synchronizer.
*/
lazy val topupTargets: Seq[(SynchronizerAlias, BuyExtraTrafficConfig)] =
((global.alias, global.buyExtraTraffic) +: extra.map(e => (e.alias, e.topup)))
.filter(_._2.targetThroughput.value > 0)
}

final case class MigrateValidatorPartyConfig(
// The scan instance the ACS snapshot should be fetched from.
Expand Down
6 changes: 6 additions & 0 deletions cluster/helm/splice-validator/templates/validator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,12 @@ spec:
canton.validator-apps.validator_backend.domains.extra.{{ $ii }} = {
alias = {{ $domain.alias | quote }}
url = {{ $domain.url | quote }}
{{- with $domain.topup }}
topup {
target-throughput = {{ .targetThroughput }}
min-topup-interval = {{ .minTopupInterval }}
}
{{- end }}
}
{{- end }}
{{- if (.Values.topup).enabled }}
Expand Down
32 changes: 32 additions & 0 deletions cluster/helm/splice-validator/tests/validator_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,38 @@ tests:
path: spec.template.spec.containers[?(@.name=='validator-app')].env[?(@.name=='ADDITIONAL_CONFIG_SYNCHRONIZER_URL')].value
pattern: 'canton\.validator-apps\.validator_backend\.domains\.global\.url = "https://new-sequencer\.mock\.com"'

- it: "renders extra domain topup settings when provided"
set:
extraDomains:
- alias: "dedicated-1"
url: "https://dedicated-1.mock.com"
topup:
targetThroughput: 5000
minTopupInterval: "1m"
documentSelector:
path: kind
value: Deployment
asserts:
- matchRegex:
path: spec.template.spec.containers[?(@.name=='validator-app')].env[?(@.name=='ADDITIONAL_CONFIG_EXTRA_DOMAIN_0')].value
pattern: 'target-throughput = 5000'
- matchRegex:
path: spec.template.spec.containers[?(@.name=='validator-app')].env[?(@.name=='ADDITIONAL_CONFIG_EXTRA_DOMAIN_0')].value
pattern: 'min-topup-interval = 1m'

- it: "omits the extra domain topup block when not provided"
set:
extraDomains:
- alias: "dedicated-1"
url: "https://dedicated-1.mock.com"
documentSelector:
path: kind
value: Deployment
asserts:
- notMatchRegex:
path: spec.template.spec.containers[?(@.name=='validator-app')].env[?(@.name=='ADDITIONAL_CONFIG_EXTRA_DOMAIN_0')].value
pattern: 'topup'

- it: "uses synchronizer.svNames as a JSON array when provided"
set:
synchronizer:
Expand Down
34 changes: 21 additions & 13 deletions cluster/helm/splice-validator/values.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -250,18 +250,7 @@
"required": ["topup"],
"properties": {
"topup": {
"type": "object",
"required": ["targetThroughput", "minTopupInterval"],
"properties": {
"targetThroughput": {
"type": "integer",
"minimum": 0
},
"minTopupInterval": {
"type": "string",
"pattern": "^[0-9]+[smh]$"
}
}
"$ref": "#/$defs/topup"
}
}
}
Expand Down Expand Up @@ -496,6 +485,9 @@
},
"url": {
"type": "string"
},
"topup": {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

factor this out into a def and reuse it between the global synchronizer schema and this

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

"$ref": "#/$defs/topup"
}
}
}
Expand Down Expand Up @@ -781,5 +773,21 @@
]
}
}
]
],
"$defs": {
"topup": {
"type": "object",
"required": ["targetThroughput", "minTopupInterval"],
"properties": {
"targetThroughput": {
"type": "integer",
"minimum": 0
},
"minTopupInterval": {
"type": "string",
"pattern": "^[0-9]+[smh]$"
}
}
}
}
}
Loading