Skip to content

Commit f71b6d0

Browse files
[release-line-3.4] Update 2026-01-06.21
Reference commit: f27b8e1c49
1 parent 7f0e956 commit f71b6d0

File tree

15 files changed

+1221
-178
lines changed

15 files changed

+1221
-178
lines changed

community/LICENSE-open-source-bundle.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@
186186
same "printed page" as the copyright notice for easier
187187
identification within third-party archives.
188188

189-
Copyright (c) 2025 Digital Asset (Switzerland) GmbH and/or its affiliates
189+
Copyright (c) 2026 Digital Asset (Switzerland) GmbH and/or its affiliates
190190

191191
Licensed under the Apache License, Version 2.0 (the "License");
192192
you may not use this file except in compliance with the License.

community/app/src/test/scala/com/digitalasset/canton/integration/tests/examples/OpenSslOfflineRootKeyDemoExampleIntegrationTest.scala

Lines changed: 71 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -7,30 +7,39 @@ import better.files.File
77
import com.daml.nonempty.NonEmpty
88
import com.digitalasset.canton.crypto.Fingerprint
99
import com.digitalasset.canton.integration.CommunityIntegrationTest
10-
import com.digitalasset.canton.integration.plugins.UseH2
1110
import com.digitalasset.canton.integration.tests.examples.ExampleIntegrationTest.examplesPath
1211
import com.digitalasset.canton.integration.tests.examples.OpenSslOfflineRootKeyDemoExampleIntegrationTest.demoFolder
1312
import com.digitalasset.canton.topology.admin.grpc.TopologyStoreId.Authorized
1413
import com.digitalasset.canton.topology.transaction.{DelegationRestriction, TopologyMapping}
14+
import org.scalatest.BeforeAndAfterEach
1515
import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
1616

1717
object OpenSslOfflineRootKeyDemoExampleIntegrationTest {
1818
lazy val demoFolder: File = examplesPath / "10-offline-root-namespace-init"
1919
}
2020

21-
sealed abstract class OpenSslOfflineRootKeyDemoExampleIntegrationTest
21+
class OpenSslOfflineRootKeyDemoExampleIntegrationTest
2222
extends ExampleIntegrationTest(
2323
demoFolder / "manual-init-example.conf"
2424
)
2525
with CommunityIntegrationTest
26-
with ScalaCheckPropertyChecks {
26+
with ScalaCheckPropertyChecks
27+
with BeforeAndAfterEach {
2728

2829
override def afterAll(): Unit = {
2930
super.afterAll()
3031
// Delete the temp files created by the test
3132
(demoFolder / "tmp").delete(swallowIOExceptions = true)
3233
}
3334

35+
override def afterEach(): Unit =
36+
List(
37+
"canton-examples.init-script",
38+
"canton-examples.openssl-signature-algorithm",
39+
"canton-examples.openssl-script-dir",
40+
"canton-examples.openssl-keys-dir",
41+
).foreach(System.clearProperty)
42+
3443
private def delegationRestrictions(
3544
fingerprint: Fingerprint
3645
)(implicit env: FixtureParam) = {
@@ -45,79 +54,71 @@ sealed abstract class OpenSslOfflineRootKeyDemoExampleIntegrationTest
4554
.restriction
4655
}
4756

48-
"run offline root namespace key init demo" in { implicit env =>
49-
import env.*
57+
List(
58+
("openssl-example-ec256.sh", "ecdsa256"),
59+
("openssl-example-ed25519.sh", "ed25519"),
60+
).foreach { case (script, keySpec) =>
61+
s"run offline root namespace key init demo for $keySpec" in { implicit env =>
62+
import env.*
5063

51-
val tmpDir = better.files.File.newTemporaryDirectory("tmp")
52-
ExampleIntegrationTest.ensureSystemProperties(
53-
"canton-examples.openssl-script-dir" -> demoFolder.pathAsString
54-
)
55-
ExampleIntegrationTest.ensureSystemProperties(
56-
"canton-examples.openssl-keys-dir" -> tmpDir.pathAsString
57-
)
58-
runScript(demoFolder / "bootstrap.canton")(environment)
59-
participant1.is_initialized shouldBe true
60-
61-
// Check root key restrictions
62-
delegationRestrictions(
63-
participant1.id.fingerprint
64-
) shouldBe DelegationRestriction.CanSignAllMappings
65-
66-
val namespaceDelegationFingerprint = participant1.keys.public
67-
.list()
68-
.find(_.name.map(_.unwrap).contains("IntermediateKey"))
69-
.value
70-
.id
71-
72-
// Check intermediate key restrictions
73-
delegationRestrictions(
74-
namespaceDelegationFingerprint
75-
) shouldBe DelegationRestriction.CanSignAllButNamespaceDelegations
76-
77-
// Run the script adding a key with signing restrictions
78-
runScript(demoFolder / "restricted-key.canton")(environment)
79-
80-
// Check restricted key restrictions
81-
delegationRestrictions(
82-
participant1.keys.public
64+
ExampleIntegrationTest.ensureSystemProperties(
65+
"canton-examples.init-script" -> script,
66+
"canton-examples.openssl-signature-algorithm" -> keySpec,
67+
)
68+
69+
val tmpDir = better.files.File.newTemporaryDirectory("tmp")
70+
ExampleIntegrationTest.ensureSystemProperties(
71+
"canton-examples.openssl-script-dir" -> demoFolder.pathAsString
72+
)
73+
ExampleIntegrationTest.ensureSystemProperties(
74+
"canton-examples.openssl-keys-dir" -> tmpDir.pathAsString
75+
)
76+
runScript(demoFolder / "bootstrap.canton")(environment)
77+
participant1.is_initialized shouldBe true
78+
79+
// Check root key restrictions
80+
delegationRestrictions(
81+
participant1.id.fingerprint
82+
) shouldBe DelegationRestriction.CanSignAllMappings
83+
84+
val namespaceDelegationFingerprint = participant1.keys.public
8385
.list()
84-
.find(_.name.map(_.unwrap).contains("RestrictedKey"))
86+
.find(_.name.map(_.unwrap).contains("IntermediateKey"))
8587
.value
8688
.id
87-
) shouldBe DelegationRestriction.CanSignSpecificMappings(
88-
NonEmpty.mk(
89-
Set,
90-
TopologyMapping.Code.PartyToParticipant,
91-
TopologyMapping.Code.PartyToKeyMapping,
89+
90+
// Check intermediate key restrictions
91+
delegationRestrictions(
92+
namespaceDelegationFingerprint
93+
) shouldBe DelegationRestriction.CanSignAllButNamespaceDelegations
94+
95+
// Run the script adding a key with signing restrictions
96+
runScript(demoFolder / "restricted-key.canton")(environment)
97+
98+
// Check restricted key restrictions
99+
delegationRestrictions(
100+
participant1.keys.public
101+
.list()
102+
.find(_.name.map(_.unwrap).contains("RestrictedKey"))
103+
.value
104+
.id
105+
) shouldBe DelegationRestriction.CanSignSpecificMappings(
106+
NonEmpty.mk(
107+
Set,
108+
TopologyMapping.Code.PartyToParticipant,
109+
TopologyMapping.Code.PartyToKeyMapping,
110+
)
92111
)
93-
)
94112

95-
// Run the script revoking the delegation
96-
runScript(demoFolder / "revoke-namespace-delegation.canton")(environment)
113+
// Run the script revoking the delegation
114+
runScript(demoFolder / "revoke-namespace-delegation.canton")(environment)
97115

98-
// Check the delegation is gone
99-
participant1.topology.namespace_delegations
100-
.list(
101-
store = Authorized,
102-
filterTargetKey = Some(namespaceDelegationFingerprint),
103-
) shouldBe empty
116+
// Check the delegation is gone
117+
participant1.topology.namespace_delegations
118+
.list(
119+
store = Authorized,
120+
filterTargetKey = Some(namespaceDelegationFingerprint),
121+
) shouldBe empty
122+
}
104123
}
105124
}
106-
107-
final class OpenSslOfflineRootKeyDemoExampleIntegrationTestEc256
108-
extends OpenSslOfflineRootKeyDemoExampleIntegrationTest {
109-
registerPlugin(new UseH2(loggerFactory))
110-
ExampleIntegrationTest.ensureSystemProperties(
111-
"canton-examples.init-script" -> "openssl-example-ec256.sh",
112-
"canton-examples.openssl-signature-algorithm" -> "ecdsa256",
113-
)
114-
}
115-
116-
final class OpenSslOfflineRootKeyDemoExampleIntegrationTestEd25519
117-
extends OpenSslOfflineRootKeyDemoExampleIntegrationTest {
118-
registerPlugin(new UseH2(loggerFactory))
119-
ExampleIntegrationTest.ensureSystemProperties(
120-
"canton-examples.init-script" -> "openssl-example-ed25519.sh",
121-
"canton-examples.openssl-signature-algorithm" -> "ed25519",
122-
)
123-
}

community/base/src/main/scala/com/digitalasset/canton/serialization/ProtoConverter.scala

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,15 +80,19 @@ object ProtoConverter {
8080
*/
8181
def protoParser[A](parseFrom: CodedInputStream => A): ByteString => Either[BufferException, A] =
8282
bytes =>
83-
Either
84-
.catchOnly[InvalidProtocolBufferException](parseFrom(bytes.newCodedInput))
85-
.leftMap(BufferException.apply)
83+
try {
84+
Right(parseFrom(bytes.newCodedInput))
85+
} catch {
86+
case e: InvalidProtocolBufferException => Left(BufferException(e))
87+
}
8688

8789
def protoParserArray[A](parseFrom: Array[Byte] => A): Array[Byte] => Either[BufferException, A] =
8890
bytes =>
89-
Either
90-
.catchOnly[InvalidProtocolBufferException](parseFrom(bytes))
91-
.leftMap(BufferException.apply)
91+
try {
92+
Right(parseFrom(bytes))
93+
} catch {
94+
case e: InvalidProtocolBufferException => Left(BufferException(e))
95+
}
9296

9397
/** Helper for extracting an optional field where the value is required
9498
* @param field

community/base/src/main/scala/com/digitalasset/canton/util/HexString.scala

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,19 @@ object HexString {
2121
)
2222
}
2323

24-
def toHexString(bytes: Array[Byte]): String = bytes.map(b => f"$b%02x").mkString("")
24+
private val hexArray = "0123456789abcdef".toCharArray
25+
@SuppressWarnings(Array("org.wartremover.warts.Var", "org.wartremover.warts.While"))
26+
def toHexString(bytes: Array[Byte]): String = {
27+
val hexChars = new Array[Char](bytes.length * 2)
28+
var ii = 0
29+
while (ii < bytes.length) {
30+
val v = bytes(ii) & 0xff // mask to unsigned int
31+
hexChars(ii * 2) = hexArray(v >>> 4) // leading bits
32+
hexChars(ii * 2 + 1) = hexArray(v & 0x0f) // trailing bits
33+
ii += 1
34+
}
35+
new String(hexChars)
36+
}
2537

2638
/** Parse a hex-string `s` to a byte array. */
2739
@SuppressWarnings(Array("org.wartremover.warts.AsInstanceOf"))

community/participant/src/main/scala/com/digitalasset/canton/participant/event/AcsChangeListener.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33

44
package com.digitalasset.canton.participant.event
55

6+
import com.daml.nonempty.NonEmpty
67
import com.digitalasset.canton.ledger.participant.state.{
78
AcsChange,
89
AcsChangeFactory,
910
AcsChangeFactoryImpl,
1011
ContractStakeholdersAndReassignmentCounter,
1112
}
13+
import com.digitalasset.canton.lifecycle.FutureUnlessShutdown
1214
import com.digitalasset.canton.logging.{HasLoggerName, NamedLoggingContext}
1315
import com.digitalasset.canton.participant.protocol.conflictdetection.CommitSet
1416
import com.digitalasset.canton.tracing.TraceContext
@@ -30,6 +32,10 @@ trait AcsChangeListener {
3032
traceContext: TraceContext
3133
): Unit
3234

35+
def publish(acsChanges: NonEmpty[Seq[(RecordTime, AcsChange)]])(implicit
36+
traceContext: TraceContext
37+
): FutureUnlessShutdown[Unit]
38+
3339
def publish(toc: RecordTime, acsChangeFactoryO: Option[AcsChangeFactory])(implicit
3440
traceContext: TraceContext
3541
): Unit

0 commit comments

Comments
 (0)