Skip to content

Commit 8c6506c

Browse files
canton-network-daDA Automation
andauthored
[ci] Update Splice from CCI (#426)
Signed-off-by: DA Automation <splice-maintainers@digitalasset.com> Co-authored-by: DA Automation <splice-maintainers@digitalasset.com>
1 parent a5b0746 commit 8c6506c

File tree

235 files changed

+1549
-1633
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

235 files changed

+1549
-1633
lines changed

.github/workflows/build.deployment_test.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,12 @@ jobs:
2929
artifactory_user: ${{ vars.ARTIFACTORY_USER }}
3030
artifactory_password: ${{ secrets.ARTIFACTORY_PASSWORD }}
3131

32-
- name: Deployment test
32+
- name: Helm tests
33+
uses: ./.github/actions/nix/run_bash_command_in_nix
34+
with:
35+
cmd: make cluster/helm/test
36+
37+
- name: Pulumi tests
3338
uses: ./.github/actions/nix/run_bash_command_in_nix
3439
with:
3540
cmd_retry_count: 5 # Retry in case of dependency download errors

.github/workflows/notify_readme_changes.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
echo "FOUND_DOCUMENT_CHANGES=true" ;
2727
echo "GITHUB_COMMIT_URL=\"https://github.com/DACH-NY/canton-network-node/commit/${commit}\"" >> "$GITHUB_OUTPUT"
2828
echo "CHANGED_DOC_FILES=\"${changed_files_list}\""
29-
echo "GIT_LOG=\"$(git log -1 --oneline --no-color | sed s/\"//g)\""
29+
echo "GIT_LOG=\"$(git log -1 --oneline --no-color | sed s/\"//g | sed s/\`//g)\""
3030
} >> "$GITHUB_OUTPUT"
3131
else
3232
echo "No document changes are detected."

TESTING.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
- [Handling Errors in Integration Tests](#handling-errors-in-integration-tests)
2323
- [Connecting external tools to the shared Canton instances](#connecting-external-tools-to-the-shared-canton-instances)
2424
- [Testing App Upgrades](#testing-app-upgrades)
25+
- [Deployment Tests](#deployment-tests)
2526

2627
# Testing in Splice
2728

@@ -35,6 +36,7 @@ Splice code is tested in the following ways:
3536
- Integration tests. Extensive integration tests are located under `apps/app/src/test/scala/`. Integration tests
3637
include tests that use frontends (whose names must end with `FrontendIntegrationTest`), and ones which do not
3738
(whose names ends with IntegrationTest).
39+
- [Deployment tests](#deployment-tests) to catch errors in Helm and Pulumi before deploying to a cluster.
3840
- Cluster tests. Various different cluster tests are currently run by Digital Asset on Splice codebase.
3941
This includes:
4042
- Deploying and testing a PR on a scratch cluster. See (TBD)
@@ -365,3 +367,26 @@ PRs/commits that include `[breaking]` in their commit message, or that bump the
365367

366368
The test spins up a full network in the source version, creates some activity, then gradually upgrades several of the components (SVs and validators)
367369
one-by-one to the current commit's version.
370+
371+
## Deployment Tests
372+
373+
Static deployment tests are run on every commit to `main` and on every PR tagged with `[static]` or `[ci]`.
374+
They guard against unintended changes to deployed state resulting from changes to Helm charts and Pulumi deployment scripts.
375+
The tests described here are **not a replacement for testing via cluster deployment**.
376+
They are meant to provide a quick feedback loop and to offer additional protection against regressions for code paths that are not sufficiently well covered by automatic cluster tests.
377+
378+
### Helm checks
379+
380+
We use [helm-unittest](https://github.com/helm-unittest/helm-unittest/) for some of our Helm charts.
381+
To run all Helm chart tests locally run `make cluster/helm/test`.
382+
To run only the tests for a specific chart `CHART`, run `helm unittest cluster/helm/CHART`.
383+
384+
Refer to the documentation of `helm-unittest` for more information on how to extend our Helm tests.
385+
When writing or debugging Helm tests, it is often useful to run `helm template` to see the rendered templates.
386+
387+
### Pulumi checks
388+
389+
Our pulumi checks are based on checked in `expected` files that need to be updated whenever the expected deployment state changes.
390+
391+
Please run `make cluster/pulumi/update-expected` whenever you intend to change Pulumi deployment scripts in a way that alters deployed state.
392+
Compare the diff of the resulting `expected` files to confirm that the changes are as intended.

apps/app/src/main/scala/org/lfdecentralizedtrust/splice/config/SpliceConfig.scala

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -593,13 +593,6 @@ object SpliceConfig {
593593
"initialPackageConfig is not valid due to inconsistent dependencies"
594594
),
595595
)
596-
_ <- Either.cond(
597-
conf.synchronizerNodes.isEmpty || conf.supportsSoftDomainMigrationPoc,
598-
(),
599-
ConfigValidationFailed(
600-
"synchronizerNodes must be empty unless supportsSoftDomainMigrationPoc is set to true"
601-
),
602-
)
603596
_ <- Either.cond(
604597
conf.legacyMigrationId.forall(_ == conf.domainMigrationId - 1L),
605598
(),

apps/app/src/main/scala/org/lfdecentralizedtrust/splice/console/HttpCommandRunner.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ trait HttpCommandRunner {
1212
* HTTP variant of Canton’s AdminCommandRunner.
1313
*/
1414
protected[console] def httpCommand[Result](
15-
httpCommand: HttpCommand[_, Result]
15+
httpCommand: HttpCommand[_, Result],
16+
basePath: Option[String] = None,
1617
): ConsoleCommandResult[Result]
1718
}

apps/app/src/main/scala/org/lfdecentralizedtrust/splice/console/SpliceInstanceReference.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,13 +121,16 @@ trait HttpAppReference extends AppReference with HttpCommandRunner {
121121
def httpClientConfig: NetworkAppClientConfig
122122

123123
override protected[splice] def httpCommand[Result](
124-
httpCommand: HttpCommand[_, Result]
124+
httpCommand: HttpCommand[_, Result],
125+
basePath: Option[String] = None,
125126
): ConsoleCommandResult[Result] =
126127
spliceConsoleEnvironment.httpCommandRunner.runCommand(
127128
name,
128129
httpCommand,
129130
headers,
130-
httpClientConfig,
131+
basePath.fold(httpClientConfig)(p =>
132+
httpClientConfig.copy(url = httpClientConfig.url.withPath(httpClientConfig.url.path + p))
133+
),
131134
)
132135

133136
@Help.Summary("Health and diagnostic related commands (HTTP)")

apps/app/src/main/scala/org/lfdecentralizedtrust/splice/console/SvAppReference.scala

Lines changed: 11 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
package org.lfdecentralizedtrust.splice.console
55

6-
import com.digitalasset.canton.SynchronizerAlias
76
import org.lfdecentralizedtrust.splice.auth.AuthUtil
87
import org.lfdecentralizedtrust.splice.codegen.java.splice.round.OpenMiningRound
98
import org.lfdecentralizedtrust.splice.codegen.java.splice.dso.amuletprice as cp
@@ -24,19 +23,18 @@ import org.lfdecentralizedtrust.splice.sv.{SvApp, SvAppBootstrap, SvAppClientCon
2423
import org.lfdecentralizedtrust.splice.sv.admin.api.client.commands.{
2524
HttpSvAdminAppClient,
2625
HttpSvAppClient,
27-
HttpSvSoftDomainMigrationPocAppClient,
2826
}
2927
import org.lfdecentralizedtrust.splice.sv.automation.{
3028
DsoDelegateBasedAutomationService,
3129
SvDsoAutomationService,
3230
}
33-
import org.lfdecentralizedtrust.splice.sv.config.{SvAppBackendConfig, SvSynchronizerNodeConfig}
31+
import org.lfdecentralizedtrust.splice.sv.config.SvAppBackendConfig
3432
import org.lfdecentralizedtrust.splice.sv.migration.{DomainDataSnapshot, SynchronizerNodeIdentities}
3533
import org.lfdecentralizedtrust.splice.sv.util.ValidatorOnboarding
3634
import org.lfdecentralizedtrust.splice.util.Contract
3735
import com.digitalasset.canton.admin.api.client.data.NodeStatus
3836
import com.digitalasset.canton.console.{BaseInspection, Help}
39-
import com.digitalasset.canton.topology.{SynchronizerId, ParticipantId, PartyId}
37+
import com.digitalasset.canton.topology.{ParticipantId, PartyId}
4038
import com.digitalasset.canton.tracing.TraceContext
4139
import org.apache.pekko.actor.ActorSystem
4240

@@ -400,20 +398,6 @@ class SvAppBackendReference(
400398
httpCommand(HttpSvAdminAppClient.GetMediatorNodeStatus())
401399
}
402400

403-
def reconcileSynchronizerDamlState(synchronizerIdPrefix: String): Unit =
404-
consoleEnvironment.run {
405-
httpCommand(
406-
HttpSvSoftDomainMigrationPocAppClient.ReconcileSynchronizerDamlState(synchronizerIdPrefix)
407-
)
408-
}
409-
410-
def signDsoPartyToParticipant(synchronizerIdPrefix: String): Unit =
411-
consoleEnvironment.run {
412-
httpCommand(
413-
HttpSvSoftDomainMigrationPocAppClient.SignDsoPartyToParticipant(synchronizerIdPrefix)
414-
)
415-
}
416-
417401
/** Remote participant this sv app is configured to interact with. */
418402
lazy val participantClient =
419403
new ParticipantClientReference(
@@ -430,65 +414,21 @@ class SvAppBackendReference(
430414
config.participantClient.participantClientConfigWithAdminToken,
431415
)
432416

433-
def sequencerClient(synchronizerId: SynchronizerId): SequencerClientReference = {
434-
val synchronizerConfig = synchronizerConfigForDomain(synchronizerId)
435-
new SequencerClientReference(
436-
consoleEnvironment,
437-
s"sequencer client for $name for domain $synchronizerId",
438-
synchronizerConfig.sequencer.toCantonConfig,
439-
)
440-
}
417+
private def localSynchronizerNode = config.localSynchronizerNode.getOrElse(
418+
throw new RuntimeException("No synchronizer node configured for SV app")
419+
)
441420

442-
def sequencerClient(synchronizerAlias: SynchronizerAlias): SequencerClientReference = {
443-
val synchronizerConfig: SvSynchronizerNodeConfig = synchronizerConfigForDomain(
444-
synchronizerAlias
445-
)
421+
lazy val sequencerClient: SequencerClientReference =
446422
new SequencerClientReference(
447423
consoleEnvironment,
448-
s"sequencer client for $name for domain $synchronizerAlias",
449-
synchronizerConfig.sequencer.toCantonConfig,
424+
s"sequencer client for $name",
425+
localSynchronizerNode.sequencer.toCantonConfig,
450426
)
451-
}
452-
453-
def mediatorClient(domainId: SynchronizerId): MediatorClientReference = {
454-
val synchronizerConfig: SvSynchronizerNodeConfig = synchronizerConfigForDomain(domainId)
455-
new MediatorClientReference(
456-
consoleEnvironment,
457-
s"mediator client for $name for domain $domainId",
458-
synchronizerConfig.mediator.toCantonConfig,
459-
)
460-
}
461427

462-
def mediatorClient(synchronizerAlias: SynchronizerAlias): MediatorClientReference = {
463-
val synchronizerConfig: SvSynchronizerNodeConfig = synchronizerConfigForDomain(
464-
synchronizerAlias
465-
)
428+
lazy val mediatorClient: MediatorClientReference =
466429
new MediatorClientReference(
467430
consoleEnvironment,
468-
s"mediator client for $name for domain $synchronizerAlias",
469-
synchronizerConfig.mediator.toCantonConfig,
431+
s"mediator client for $name",
432+
localSynchronizerNode.mediator.toCantonConfig,
470433
)
471-
}
472-
473-
private def synchronizerConfigForDomain(alias: SynchronizerAlias) = {
474-
val synchronizerConfig = config.synchronizerNodes.get(alias.toProtoPrimitive) match {
475-
case Some(synchronizer) => synchronizer
476-
case None =>
477-
config.localSynchronizerNode.getOrElse(
478-
throw new RuntimeException("No sequencer admin connection configured for SV App")
479-
)
480-
}
481-
synchronizerConfig
482-
}
483-
484-
private def synchronizerConfigForDomain(domainId: SynchronizerId) = {
485-
val synchronizerConfig = config.synchronizerNodes.get(domainId.uid.identifier.str) match {
486-
case Some(synchronizer) => synchronizer
487-
case None =>
488-
config.localSynchronizerNode.getOrElse(
489-
throw new RuntimeException("No sequencer admin connection configured for SV App")
490-
)
491-
}
492-
synchronizerConfig
493-
}
494434
}

apps/app/src/main/scala/org/lfdecentralizedtrust/splice/console/ValidatorAppReference.scala

Lines changed: 127 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@ import org.lfdecentralizedtrust.splice.http.v0.definitions.{
1515
SignedTopologyTx,
1616
}
1717
import org.lfdecentralizedtrust.splice.identities.NodeIdentitiesDump
18-
import org.lfdecentralizedtrust.splice.util.ContractWithState
18+
import org.lfdecentralizedtrust.splice.scan.admin.api.client.commands.HttpScanAppClient
19+
import org.lfdecentralizedtrust.splice.util.{
20+
ChoiceContextWithDisclosures,
21+
ContractWithState,
22+
FactoryChoiceWithDisclosures,
23+
}
1924
import org.lfdecentralizedtrust.splice.validator.admin.api.client.commands.*
2025
import org.lfdecentralizedtrust.splice.validator.automation.ValidatorAutomationService
2126
import org.lfdecentralizedtrust.splice.validator.config.{
@@ -25,11 +30,17 @@ import org.lfdecentralizedtrust.splice.validator.config.{
2530
import org.lfdecentralizedtrust.splice.validator.migration.DomainMigrationDump
2631
import org.lfdecentralizedtrust.splice.validator.{ValidatorApp, ValidatorAppBootstrap}
2732
import org.lfdecentralizedtrust.splice.wallet.automation.UserWalletAutomationService
33+
import org.lfdecentralizedtrust.tokenstandard.{metadata, transferinstruction}
2834
import com.digitalasset.canton.console.{BaseInspection, Help}
2935
import com.digitalasset.canton.data.CantonTimestamp
3036
import com.digitalasset.canton.topology.PartyId
3137
import org.apache.pekko.actor.ActorSystem
3238
import org.lfdecentralizedtrust.splice.codegen.java.splice.amuletrules.TransferPreapproval
39+
import org.lfdecentralizedtrust.splice.codegen.java.splice.api.token.{
40+
allocationv1,
41+
allocationinstructionv1,
42+
transferinstructionv1,
43+
}
3344
import org.lfdecentralizedtrust.splice.codegen.java.splice.externalpartyamuletrules.TransferCommandCounter
3445

3546
import java.time.Instant
@@ -361,6 +372,121 @@ abstract class ValidatorAppReference(
361372
)
362373
}
363374
}
375+
376+
private val scanProxyPrefix = "/api/validator/v0/scan-proxy"
377+
378+
def getRegistryInfo(): metadata.v1.definitions.GetRegistryInfoResponse = {
379+
consoleEnvironment.run {
380+
httpCommand(
381+
HttpScanAppClient.GetRegistryInfo,
382+
Some(scanProxyPrefix),
383+
)
384+
}
385+
}
386+
387+
def lookupInstrument(instrumentId: String) =
388+
consoleEnvironment.run {
389+
httpCommand(HttpScanAppClient.LookupInstrument(instrumentId), Some(scanProxyPrefix))
390+
}
391+
392+
def listInstruments() =
393+
consoleEnvironment.run {
394+
httpCommand(
395+
HttpScanAppClient.ListInstruments(pageSize = None, pageToken = None),
396+
Some(scanProxyPrefix),
397+
)
398+
}
399+
400+
def getTransferFactory(
401+
choiceArgs: transferinstructionv1.TransferFactory_Transfer
402+
): (
403+
FactoryChoiceWithDisclosures[
404+
transferinstructionv1.TransferFactory.ContractId,
405+
transferinstructionv1.TransferFactory_Transfer,
406+
],
407+
transferinstruction.v1.definitions.TransferFactoryWithChoiceContext.TransferKind,
408+
) = {
409+
consoleEnvironment.run {
410+
httpCommand(HttpScanAppClient.GetTransferFactory(choiceArgs), Some(scanProxyPrefix))
411+
}
412+
}
413+
414+
def getTransferInstructionAcceptContext(
415+
transferInstructionId: transferinstructionv1.TransferInstruction.ContractId
416+
): ChoiceContextWithDisclosures = {
417+
consoleEnvironment.run {
418+
httpCommand(
419+
HttpScanAppClient.GetTransferInstructionAcceptContext(transferInstructionId),
420+
Some(scanProxyPrefix),
421+
)
422+
}
423+
}
424+
425+
def getTransferInstructionRejectContext(
426+
transferInstructionId: transferinstructionv1.TransferInstruction.ContractId
427+
): ChoiceContextWithDisclosures = {
428+
consoleEnvironment.run {
429+
httpCommand(
430+
HttpScanAppClient.GetTransferInstructionRejectContext(transferInstructionId),
431+
Some(scanProxyPrefix),
432+
)
433+
}
434+
}
435+
436+
def getTransferInstructionWithdrawContext(
437+
transferInstructionId: transferinstructionv1.TransferInstruction.ContractId
438+
): ChoiceContextWithDisclosures = {
439+
consoleEnvironment.run {
440+
httpCommand(
441+
HttpScanAppClient.GetTransferInstructionWithdrawContext(transferInstructionId),
442+
Some(scanProxyPrefix),
443+
)
444+
}
445+
}
446+
447+
def getAllocationFactory(
448+
choiceArgs: allocationinstructionv1.AllocationFactory_Allocate
449+
): FactoryChoiceWithDisclosures[
450+
allocationinstructionv1.AllocationFactory.ContractId,
451+
allocationinstructionv1.AllocationFactory_Allocate,
452+
] = {
453+
consoleEnvironment.run {
454+
httpCommand(HttpScanAppClient.GetAllocationFactory(choiceArgs), Some(scanProxyPrefix))
455+
}
456+
}
457+
458+
def getAllocationTransferContext(
459+
allocationId: allocationv1.Allocation.ContractId
460+
): ChoiceContextWithDisclosures = {
461+
consoleEnvironment.run {
462+
httpCommand(
463+
HttpScanAppClient.GetAllocationTransferContext(allocationId),
464+
Some(scanProxyPrefix),
465+
)
466+
}
467+
}
468+
469+
def getAllocationCancelContext(
470+
allocationId: allocationv1.Allocation.ContractId
471+
): ChoiceContextWithDisclosures = {
472+
consoleEnvironment.run {
473+
httpCommand(
474+
HttpScanAppClient.GetAllocationCancelContext(allocationId),
475+
Some(scanProxyPrefix),
476+
)
477+
}
478+
}
479+
480+
def getAllocationWithdrawContext(
481+
allocationId: allocationv1.Allocation.ContractId
482+
): ChoiceContextWithDisclosures = {
483+
consoleEnvironment.run {
484+
httpCommand(
485+
HttpScanAppClient.GetAllocationWithdrawContext(allocationId),
486+
Some(scanProxyPrefix),
487+
)
488+
}
489+
}
364490
}
365491
}
366492

0 commit comments

Comments
 (0)