How can a participant connect to 2 different domain?

Hi, I have canton config like below
canton {
features {
enable-testing-commands=yes
}

domain-managers {
myDomain1 {
storage {
type = memory
}
admin-api.port = 5019
}
myDomain2 {
storage {
type = memory
}
admin-api.port = 5020
}
}
mediators.mediator1 {}
mediators.mediator2 {}

participants {
participant1 {
storage {
type = memory
}
admin-api {
port= 5012
address = “0.0.0.0”
}
ledger-api {
port = 5011
address = “0.0.0.0”
}
}

participant2 {
  storage {
    type = memory
  }
  admin-api {
    port= 5022
    address = "0.0.0.0"
  }
  ledger-api {
    port = 5021
    address = "0.0.0.0"
  }
}

participant3 {
  storage {
    type = memory
  }
  admin-api {
    port= 5032
    address = "0.0.0.0"
  }
  ledger-api {
    port = 5031
    address = "0.0.0.0"
  }
}

}
sequencers.ethereumSequencer1 {

public-api.port = 3001
admin-api.port = 4001
storage {
    type = memory
}

sequencer {
    type = "ethereum"
    config {
        ethereum-client {
          type = "besu"
        }
        client-conf {
            client-host="besu-1"
            client-port=8550
        }
        authorization-enabled=false
        contract {
          type = "automatic-deployment"
        }
     }
  }
}

}
demo.canton script like below:
import java.time.Duration

nodes.local.start()

myDomain1.setup.bootstrap_domain(List(sequencers.all(0)), List(mediator1))
myDomain2.setup.bootstrap_domain(List(sequencers.all(1)), List(mediator2))

println(s"Initialized local nodes: ${nodes.local.map(_.name).mkString(", “)}”)

println(“Running demo script”)

participant1.domains.connect_local(sequencers.all(0))
participant1.domains.connect_local(sequencers.all(1))
participant2.domains.connect_local(sequencers.all(1))
participant3.domains.connect_local(sequencers.all(0))

utils.retry_until_true {
participant2.domains.active(myDomain2)
}

val pingTime = participant1.health.ping(participant3, timeout = 60.seconds)
println(s"participant1 pings participant3 in $pingTime")

println("*“30)
println(s"Successfully initialized Canton-on-Fabric")
println("
”*30)

When I run the canton, I have this error:

Compiling /canton/Main.sc
WARN o.h.f.s.h.Config - Failed to load any configuration from: config.properties. Using toolkit defaults
Initialized local nodes: participant3, participant1, participant2, myDomain2, myDomain1, fabricSequencer2, fabricSequencer1, ethereumSequencer1, mediator2, mediator1
Running demo script
ERROR c.d.c.c.EnterpriseConsoleEnvironment - Request failed for participant1.
GrpcRequestRefusedByServer: FAILED_PRECONDITION/INCOMPATIBLE_UNIQUE_CONTRACT_KEYS_MODE(9,e678035e): Cannot connect to domain fabricSequencer1 as the participant has UCK semantics enabled and has already been connected to other domains: myDomain1::122088838680…
Request: ConnectDomain(Domain ‘fabricSequencer1’,false)
CorrelationId: e678035eed7bf4f6a911645804a4080a
Context: Map(participant → participant1, domain → fabricSequencer1)
Command ParticipantAdministration$domains$.connect_local invoked from Main.sc:13
ERROR c.d.c.ConsoleInteractiveRunner - Running bootstrap script failed with an exception (Command execution failed.)!

I refer Error codes — Daml SDK 2.2.0 documentation as below:

INCOMPATIBLE_UNIQUE_CONTRACT_KEYS_MODE

  • Explanation: This error indicates that the domain this participant is trying to connect to is a domain where unique contract keys are supported, while this participant is already connected to other domains. Multiple domains and unique contract keys are mutually exclusive features.
  • Resolution: Use isolated participants for domains that require unique keys.
  • Category: InvalidGivenCurrentSystemStateOther
  • Conveyance: This error is logged with log-level INFO on the server side. This error is exposed on the API with grpc-status FAILED_PRECONDITION including a detailed error message
  • Scaladocs: [INCOMPATIBLE_UNIQUE_CONTRACT_KEYS_MODE]

Could you advice how can a participant connect to 2 different domain, with one domain connecting to fabric sequencer, the other connecting to ethereum ? thanks!

You can, but the contract key uniqueness constraint needs to be off, as it cannot be guaranteed across domains. You can read more about it here.

Thank you for the reply! I got it. After adding init.parameters.unique-contract-keys = false to participant config, it works!

Updated Daml.canton:
myDomain1.setup.bootstrap_domain(List(fabricSequencer2,fabricSequencer1), List(mediator1))
myDomain2.setup.bootstrap_domain(List(ethereumSequencer1), List(mediator2))

println(s"Initialized local nodes: ${nodes.local.map(_.name).mkString(", “)}”)

println(“Running demo script”)

participant1.domains.connect_local(fabricSequencer2)
participant2.domains.connect_local(fabricSequencer1)
participant3.domains.connect_local(fabricSequencer1)
participant3.domains.connect_local(ethereumSequencer1)

@ health.status
res7: EnterpriseCantonStatus = Status for Participant ‘remoteParticipant1’:
Participant id: PAR::participant1::12202b56d611ea89c9fd4978ad555a1459d8f9d59da3cbd435faec53808f218bfa79
Uptime: 4m 1.142719s
Ports:
ledger: 5011
admin: 5012
Connected domains:
myDomain1::12204a65cedc…
Unhealthy domains: None
Active: true

Status for Participant ‘remoteParticipant2’:
Participant id: PAR::participant2::1220a2ca69b54fa13cecf0113b554b36a56a55d96dece917e94e3a288766564b521f
Uptime: 3m 57.09882s
Ports:
ledger: 5021
admin: 5022
Connected domains:
myDomain1::12204a65cedc…
Unhealthy domains: None
Active: true

Status for Participant ‘remoteParticipant3’:
Participant id: PAR::participant3::122004dd02c8c1dd5cf833620aa30567813c9674f19b31f3d66e0a6b82754f4a05ee
Uptime: 3m 52.577516s
Ports:
ledger: 5031
admin: 5032
Connected domains:
myDomain1::12204a65cedc…
myDomain2::1220617d1115…
Unhealthy domains: None
Active: true

Follow-up questions: since a participant connects to 2 domain, when a party submits a daml contract by this participant, (for example we upload bank.dar to this participant node, and submit a creating account contract) is there a way that can control the contract goes to which domain during the run time? is that possible to call ledger json api by passing in the domain parameter to decide which domain to go?

And can daml code deployed in the participant fetch contract from both domain? if it does, how do we control to fetch which domain’s contract?

I refered to [ A multi-domain workflow](Composability — Canton 0.27.0 documentation) and Domain Architecture and Integrations , but i am still not clear about these questions

Any advice? if could provide some daml example code to connect to multiple domain, that would be better. thanks!

You’ll need to indicate, in application logic, which domain will process transactions that take this contract as input. This updates a mapping between sync domain and contracts that’s maintained on the participant node.

Note that you’re not sending the contract to the synchronization domain; you’re sending a transaction to a set of participant nodes via the synchronization domain. That transaction results in actions to archive, update or read contracts on the various participant nodes.

There are others who can give more detailed examples of how to do this via application logic.

Thank you @Wayne_Collier for the clarifcation.

1, I uploaded a dar to the particiant which connects two domains.
2. I called a json api which binds to the participant ledger api:
/v1/create
{
“templateId”: “Bank:Bank”,
“payload”: {
“alias”: “ICBC1”,
“token”: “ICDB TOKEN1”,
“bank”: “bank::1220b39ab1de78dc30ec64afbf856e0a826e15c7bdee35171acd3c4f55dd822bc3be”,
“currencyType”:{
“issuer”:“bank::1220b39ab1de78dc30ec64afbf856e0a826e15c7bdee35171acd3c4f55dd822bc3be”,
“symbol”:“USD”
}
}
}
3 I got result:
{
“result”: {
“agreementText”: “”,
“completionOffset”: “00000000000000002b”,
“contractId”: “00cf3dc3677da687b19296f42e2f249be47e92380b8d1400d35195baa31f4cb135ca001220d626ec3635b98995502f921a85ea00ffe6add296e379b3b3a85675f95f5c1dc0”,
“key”: {
“_1”: “bank::1220b39ab1de78dc30ec64afbf856e0a826e15c7bdee35171acd3c4f55dd822bc3be”,
“_2”: “ICDB TOKEN1”
},
“observers”: ,
“payload”: {
“bank”: “bank::1220b39ab1de78dc30ec64afbf856e0a826e15c7bdee35171acd3c4f55dd822bc3be”,
“alias”: “ICBC1”,
“token”: “ICDB TOKEN1”,
“currencyType”: {
“issuer”: “bank::1220b39ab1de78dc30ec64afbf856e0a826e15c7bdee35171acd3c4f55dd822bc3be”,
“symbol”: “USD”
}
},
“signatories”: [
“bank::1220b39ab1de78dc30ec64afbf856e0a826e15c7bdee35171acd3c4f55dd822bc3be”
],
“templateId”: “37436e3728939e708cd6b41231dcf789672889a18178cdf68d0a383651aa9b61:Bank:Bank”
},
“status”: 200
}

I would like to know which domain processes this create contract transaction? how to indicate which domain to process the transaction when I call the json api? can we add a domain parameter to indicate that? I didn’t find that in HTTP JSON API Service

Could you give further advice on this? thanks!