Participant Node Migration

To migrate an existing participant node connected to a domain with a non KMS-compatible provider and start using KMS external keys, you need to manually execute the following steps. The general idea is to replicate the old node into a new one that uses a KMS provider and connects to a KMS-compatible domain (e.g. running JCE with KMS supported encryption and signing keys).

First, delegate the namespace of the old participant to the new participant:

val namespaceOld = participantOld.uid.namespace.fingerprint
val namespaceNew = participantNew.uid.namespace.fingerprint

val rootNamespaceDelegationOld = participantOld.topology.namespace_delegations
  .list(filterNamespace = namespaceOld.toProtoPrimitive)
  .head
  .context
  .serialized

val namespaceKeyNew = participantNew.keys.public.download(namespaceNew)
participantOld.keys.public.upload(namespaceKeyNew, Some("pNew-namespace-key"))

// Delegate namespace of old participant to new participant
val delegation = participantOld.topology.namespace_delegations.authorize(
  ops = TopologyChangeOp.Add,
  namespace = namespaceOld,
  authorizedKey = namespaceNew,
)

participantNew.topology.load_transaction(rootNamespaceDelegationOld)
participantNew.topology.load_transaction(delegation)

Secondly, you must recreate all parties of the old participant in the new participant:

val parties = participantOld.parties.list().map(_.party)

// Disconnect from new KMS-compatible domain to prepare migration of parties and contracts
participantNew.domains.disconnect(newKmsDomainSequencer.name)

parties.foreach { party =>
  participantNew.topology.party_to_participant_mappings
    .authorize(ops = TopologyChangeOp.Add, party = party, participant = participantNew.id)
}

Finally, you need to transfer the active contracts of all the parties from the old participant to the new one and connect to the new domain:

val parties = participantOld.parties.list().map(_.party)

// Make sure domain and the old participant are quiet before exporting ACS
participantOld.domains.disconnect(oldDomainSequencer.name)
oldDomainManager.stop()
oldDomainSequencer.stop()
oldDomainMediator.stop()

// There should be no conflicts between the protocol version of the old domain and the new domain
val protocolVersion = newKmsDomainManager.config.init.domainParameters.initialProtocolVersion

File.usingTemporaryFile("participantOld-acs", suffix = ".txt") { acsFile =>
  val acsFileName = acsFile.toString

  // Export from old participant
  participantOld.repair.export_acs(
    parties = parties.toSet,
    outputFile = acsFileName,
    contractDomainRenames = Map(oldDomainId -> (newKmsDomainId, protocolVersion)),
    partiesOffboarding = false,
  )

  // Import to new participant
  participantNew.repair.import_acs(acsFileName, parties.toSet)
}

// Kill/stop the old participant
participantOld.stop()

// Connect the new participant to the new domain
participantNew.domains.reconnect(newKmsDomainSequencer.name)

The end result is a new participant node with its keys stored and managed by a KMS connected to a domain that is able to communicate using the appropriate key schemes.

You need to follow the same steps if you want to migrate a node back to using a non-KMS provider.