Skip to content
Draft
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 @@ -359,6 +359,14 @@ class SvAppBackendReference(
)
}

@Help.Summary("Grant a ValidatorLicense to a validator party (via admin API)")
def grantValidatorLicense(partyId: PartyId): Unit =
consoleEnvironment.run {
httpCommand(
HttpSvOperatorAppClient.GrantValidatorLicense(partyId)
)
}

@Help.Summary("Update CC price vote (via admin API)")
def updateAmuletPriceVote(amuletPrice: BigDecimal): Unit =
consoleEnvironment.run {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,13 +158,18 @@ class BootstrapPackageConfigIntegrationTest extends IntegrationTest with Splitwe
// This simulates an app vetting newer versions of their own DARs depending on newer splice-amulet versions
// before the SVs do so. Topology aware package selection will then force the old splice-amulet and old splitwell versions
// for composed transactions. Note that for this to work splitwell contracts must be downgradeable.

// Split into batches to avoid gRPC message size limit (10 MB)
val batchSize = 12
val versionBatches =
DarResources.splitwell.all.map(_.metadata.version).distinct.grouped(batchSize).toSeq

Seq(aliceValidatorBackend, bobValidatorBackend, splitwellValidatorBackend).foreach { p =>
p.participantClient.dars.upload_many(
DarResources.splitwell.all
.map(_.metadata.version)
.distinct
.map((v: PackageVersion) => s"daml/dars/splitwell-$v.dar")
)
versionBatches.foreach { batch =>
p.participantClient.dars.upload_many(
batch.map((v: PackageVersion) => s"daml/dars/splitwell-$v.dar")
)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,56 @@ class SvFrontendIntegrationTest
}
}

"can grant a validator license to an existing party" in { implicit env =>
withFrontEnd("sv1") { implicit webDriver =>
actAndCheck(
"sv1 operator can login and browse to the validator-onboarding tab", {
go to s"http://localhost:$sv1UIPort/validator-onboarding"
loginOnCurrentPage(sv1UIPort, sv1Backend.config.ledgerApiUser)
},
)(
"We see the grant validator license form",
_ => {
find(id("grant-license-party-address")) should not be empty
find(id("grant-validator-license")) should not be empty
},
)

val licenseRows = getLicensesTableRows
val newValidatorParty = allocateRandomSvParty("test-validator", Some(100))

actAndCheck(
"fill party address", {
inside(find(id("grant-license-party-address"))) { case Some(element) =>
element.underlying.sendKeys(newValidatorParty.toProtoPrimitive)
}
},
)(
"grant button becomes enabled",
_ => {
find(id("grant-validator-license")).value.isEnabled shouldBe true
},
)

actAndCheck(
"click the grant validator license button", {
click on "grant-validator-license"

click on "grant-license-confirmation-dialog-accept-button"
},
)(
"a new validator license row is added",
_ => {
checkValidatorLicenseRow(
licenseRows.size.toLong,
sv1Backend.getDsoInfo().svParty,
newValidatorParty,
)
},
)
}
}

"can view median amulet price and update desired amulet price by each SV" in { implicit env =>
withFrontEnd("sv1") { implicit webDriver =>
actAndCheck(
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,7 @@ class SvStateManagementIntegrationTest extends SvIntegrationTestBase with Trigge
sv1Backend.getDsoInfo().dsoRules.payload.config.decentralizedSynchronizer,
sv1Backend.getDsoInfo().dsoRules.payload.config.nextScheduledSynchronizerUpgrade,
sv1Backend.getDsoInfo().dsoRules.payload.config.voteCooldownTime,
sv1Backend.getDsoInfo().dsoRules.payload.config.voteExecutionInstructionTimeout,
)

val action: ActionRequiringConfirmation =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package org.lfdecentralizedtrust.splice.integration.tests

import cats.syntax.parallel.*
import com.digitalasset.canton.logging.SuppressingLogger.LogEntryOptionality
import com.digitalasset.canton.util.FutureInstances.*
import org.lfdecentralizedtrust.splice.codegen.java.splice
import org.lfdecentralizedtrust.splice.codegen.java.splice.dso.voteexecution.VoteExecutionInstruction
import org.lfdecentralizedtrust.splice.codegen.java.splice.dsorules.actionrequiringconfirmation.ARC_DsoRules
import org.lfdecentralizedtrust.splice.codegen.java.splice.dsorules.dsorules_actionrequiringconfirmation.{
SRARC_ConfirmSvOnboarding,
SRARC_SetConfig,
}
import org.lfdecentralizedtrust.splice.codegen.java.splice.dsorules.validatorlicensechange.VLC_ChangeWeight
import org.lfdecentralizedtrust.splice.codegen.java.splice.dsorules.{
ActionRequiringConfirmation,
DsoRulesConfig,
Expand Down Expand Up @@ -194,6 +197,7 @@ class SvTimeBasedOnboardingIntegrationTest
sv1Backend.getDsoInfo().dsoRules.payload.config.decentralizedSynchronizer,
sv1Backend.getDsoInfo().dsoRules.payload.config.nextScheduledSynchronizerUpgrade,
sv1Backend.getDsoInfo().dsoRules.payload.config.voteCooldownTime,
sv1Backend.getDsoInfo().dsoRules.payload.config.voteExecutionInstructionTimeout,
)

val action: ActionRequiringConfirmation =
Expand Down Expand Up @@ -224,5 +228,57 @@ class SvTimeBasedOnboardingIntegrationTest
},
)
}

clue("expire stale `VoteExecutionInstruction` contracts") {
// Create a vote for weight change for a party not having a ValidatorLicense
// The VoteExecutionInstruction cannot be run in this case and should be expired
val newPartyWithoutLicense = allocateRandomSvParty("test-validator-expiry")

// Ignore the warnings issued by ExecuteVoteInstructionTrigger
loggerFactory.assertLogsUnorderedOptional(
{
actAndCheck(
"Modify validator licenses",
modifyValidatorLicenses(
sv1Backend,
Seq(sv2Backend, sv3Backend),
Seq(
new VLC_ChangeWeight(
newPartyWithoutLicense.toProtoPrimitive,
BigDecimal(10.0).bigDecimal,
)
),
),
)(
"VoteExecutionInstruction is created",
_ =>
sv1Backend.participantClientWithAdminToken.ledger_api_extensions.acs
.filterJava(VoteExecutionInstruction.COMPANION)(
dsoParty,
_ => true,
) should have length 1,
)

// Advance time past the default timeout of 1 day
val clockSkew = sv1Backend.config.automation.clockSkewAutomationDelay.asJava
actAndCheck(
"Advance time past the vote execution instruction timeout",
advanceTime(JavaDuration.ofDays(2) plus clockSkew),
)(
"VoteExecutionInstruction is expired",
_ => {
sv1Backend.participantClientWithAdminToken.ledger_api_extensions.acs
.filterJava(VoteExecutionInstruction.COMPANION)(
dsoParty,
_ => true,
) shouldBe empty
},
)
},
LogEntryOptionality.OptionalMany -> (_.warningMessage should include(
"ValidatorLicense not found for validator"
)),
)
}
}
}
Loading
Loading