Canton Getting Started, but with Docker

Background

The Canton documentation includes a Canton Getting Started example with the following topology.

Notice that the two participants and the domain are running in the same Canton instance. Also notice that the Canton App is running directly on the host computer.

While working through that example I wondered, “What would it take to setup this example within three Docker containers?” The following diagram illustrates that same example with each node (two participants and one domain) running in its own, separate Docker container:

This article shows what I did to set that up. This is strictly for experimentation on your local machine, not for production purposes.

Tool versions

When I did this, I was using:

Launch the containers

  1. Create three Canton configuration files (one for each of the Canton applications.)

    • Use 0.0.0.0 for the address binding (to play nicely with the Docker networking)

    • mydomain.conf
      canton {
        domains {
          mydomain {
            storage.type = memory
            public-api.port = 5018
            public-api.address = 0.0.0.0
            admin-api.port = 5019
            admin-api.address = 0.0.0.0
          }
        }
      }
      
    • participant1.conf
      canton {
        participants {
          participant1 {
            storage.type = memory
            admin-api.port = 5012
            admin-api.address = 0.0.0.0
            ledger-api.port = 5011
            ledger-api.address = 0.0.0.0
          }
        }
      }
      
    • participant2.conf
      canton {
        participants {
          participant2 {
            storage.type = memory
            admin-api.port = 5022
            admin-api.address = 0.0.0.0
            ledger-api.port = 5021
            ledger-api.address = 0.0.0.0
          }
        }
      }
      
  2. Create two Canton bootstrap files (one for each of the Canton participants.) Each bootstrap file should do the following:

    • Connect the participants to the domain.

    • Upload the DAR (which is already included in the Canton Open Source image.)

    • Enable the parties for the participants.

    • Generate a Navigator configuration file (for our convenience when later launching Navigator.)

    • participant1.canton
       nodes.local.start()
       participant1.domains.connect("mydomain", "http://mydomain:5018")
       participant1.dars.upload("dars/CantonExamples.dar")
       participant1.parties.enable("Alice", waitForDomain=DomainChoice.All)
       utils.generate_navigator_conf(participant1, Some("./host/ui-backend-participant1.conf"))
      
    • participant2.canton
       nodes.local.start()
       participant2.domains.connect("mydomain", "http://mydomain:5018")
       participant2.dars.upload("dars/CantonExamples.dar")
       participant2.parties.enable("Bob", waitForDomain=DomainChoice.All)
       participant2.parties.enable("Bank", waitForDomain=DomainChoice.All)
       utils.generate_navigator_conf(participant2, Some("./host/ui-backend-participant2.conf"))
      
  3. Create a docker-compose.yaml file.

    • Use the digitalasset/canton-open-source image.

    • Make the local folder available within the container (so that we can conveniently provide configuration files to the container.)

    • Launch the Canton service in daemon mode, using the appropriate configuration and bootstrap file for each node.

    • Expose the various ports (so that the containers can communicate with each other.)

    • Publish the Admin and Ledger API ports to the host (so that we can connect with a Canton Console and Navigator.)

    • docker-compose.yaml
      services: 
      
        mydomain:
          image: digitalasset/canton-open-source:2.5.0
          volumes:
            - ./:/canton/host/:rw
          entrypoint: bin/canton
          command: daemon --config "host/mydomain.conf" --log-profile container
          expose:
            - 5018
            - 5019
          ports:
            - 5018:5018
            - 5019:5019
      
        participant1:
          image: digitalasset/canton-open-source:2.5.0
          volumes:
            - ./:/canton/host/:rw
          entrypoint: bin/canton
          command: daemon --config "host/participant1.conf" --bootstrap "host/participant1.canton" --log-profile container
          expose:
            - 5011
            - 5012
          ports:
            - 5011:5011
            - 5012:5012
      
        participant2:
          image: digitalasset/canton-open-source:2.5.0
          volumes:
            - ./:/canton/host/:rw
          entrypoint: bin/canton
          command: daemon --config "host/participant2.conf" --bootstrap "host/participant2.canton" --log-profile container
          expose:
            - 5021
            - 5022
          ports:
            - 5021:5021
            - 5022:5022
      
  4. Ensure that your Docker engine is running (e.g, launch Docker Desktop) and then run docker compose up --detach. The three running containers will look something like the following within the Docker Desktop window:

Connect with Canton Console

  1. Create a remote.conf file for the three nodes.

    • remote.conf
        canton {
          remote-domains {
            mydomain {
              admin-api {
                address = "localhost"
                port = "5019"
              }
              public-api {
                address = "localhost"
                port = "5018"
              }
            }
          }
          remote-participants {
            participant1 {
              admin-api {
                address = "localhost"
                port = "5012"
              }
              ledger-api {
                address = "localhost"
                port = "5011"
              }
            }
            participant2 {
              admin-api {
                address = "localhost"
                port = "5022"
              }
              ledger-api {
                address = "localhost"
                port = "5021"
              }
            }
          }
        }
      
  2. From the host machine, start a Canton Console.
    daml canton-console -c remote.conf

  3. In the Canton Console, test the connectivity.
    participant1.health.ping(participant2)

  4. In the Canton Console, confirm the parties were created.
    participant1.parties.list("Bob")

Create a contract with Navigator

  1. From the host machine, start an instance of Navigator for Participant 1.
    daml navigator server localhost 5011 --port 4000 --config-file ui-backend-participant1.conf --feature-user-management false

  2. From the host machine, start an instance of Navigator for Participant 2.
    daml navigator server localhost 5021 --port 4001 --config-file ui-backend-participant2.conf --feature-user-management false

  3. In the Canton Console, get the party id for Alice.
    mydomain.parties.list("Alice").head.party.toProtoPrimitive

  4. In Navigator, log in as the bank and issue an Iou:Iou to Alice.

  5. In Navigator, log in as Alice and see the issued Iou:Iou.

Creating an Iou:Iou for Alice on participant2, port 4001

Viewing the Iou:Iou as Alice on participant1, port 4000


Here is the GitHub branch with the source code. You can use the following command to clone it.

git clone --branch docker-canton-example https://github.com/wallacekelly-da/daml-public-demos.git