Using Circuit Templates
This guide explains how to use the circuit-template
feature of Splinter,
including explaining the concept and format of a circuit template file and
how to use the circuit-template
feature when developing an application.
Overview
A circuit template contains a set of rules which partially define a new circuit. Templates make circuit creation substantially easier, as only a minimal amount of information needs be provided–the remainder is defined in the circuit template.
Circuit templates can be used via the Splinter CLI. The
splinter-circuit-template-*
commands list, show, and display further information
about selected templates. The splinter-circuit-propose
command’s template
option utilizes a template to create a circuit proposal. Aside from the Splinter
CLI, templates may also be used to create a circuit within an application.
Splinter’s Gameroom example utilizes the circuit-template
feature within the
Gameroom daemon. This document will cover examples of both scenarios.
Template format
YAML is currently the only supported format for template files. The following is an example circuit template YAML file used by the Gameroom application:
version: v1
args:
- name: ADMIN_KEYS
required: false
default: $(SIGNER_PUB_KEY)
description: >-
Public keys used to verify transactions in the scabbard service
- name: NODES
required: true
description: "List of node IDs"
- name: SIGNER_PUB_KEY
required: false
description: "Public key of the signer"
- name: GAMEROOM_NAME
required: true
description: "Name of the gameroom"
rules:
set-management-type:
management-type: "gameroom"
create-services:
service-type: 'scabbard'
service-args:
- key: 'admin_keys'
value: [$(ADMIN_KEYS)]
- key: 'peer_services'
value: '$(ALL_OTHER_SERVICES)'
- key: 'version'
value: '2'
first-service: 'a000'
set-metadata:
encoding: json
metadata:
- key: "scabbard_admin_keys"
value: ["$(ADMIN_KEYS)"]
- key: "alias"
value: "$(GAMEROOM_NAME)"
There are three main sections in a circuit template: version
, args
, and
rules
.
Version
This describes the version of the template being used. The template’s version
determines the available rules
. The template rules are explained below.
Args
The args
section of the template shows the arguments used by the template’s
rules. Each argument shows a description of the information that is required by
the circuit template’s rules to fill in the circuit definition. A rule can
define the name
, The following argument definition is from the example YAML
above:
- name: SIGNER_PUB_KEY
required: false
description: "Public key of the signer"
This shows the definition of the SIGNER_PUB_KEY
argument, which includes a
short description string and an indicator of whether or not the argument is
required. This argument will be assigned a value when the circuit is being
created.
The value of an argument may be used to fill in other necessary information
within the template. The value of an argument can be referred to using the
command substitution syntax, for example the SIGNER_PUB_KEY
argument would
appear as $(SIGNER_PUB_KEY)
. This syntax may be used when assigning the
default
value of an argument. The following shows the definition of the
ADMIN_KEYS
argument which uses the SIGNER_PUB_KEY
argument as its default
value.
- name: ADMIN_KEYS
required: false
default: $(SIGNER_PUB_KEY)
description: >-
Public keys used to verify transactions in the scabbard service
Rules
Rules use the argument values for defining the circuit. The rules, therefore, reflect the format of the circuit the template is intended for. The version of a template determines the available rules.
Version 1.0 Available Rules
-
set-management-type
: This rule takes a single argument,management-type
, and sets the circuit’s management type. -
create-services
: This rule takes aservice-type
,service-args
andfirst-service
which are used to build the services included in a circuit. -
set-metadata
: This rule takes ametadata
and anencoding
argument. Theencoding
argument supportsJSON
. The template uses this rule to set the circuit’s metadata, using the encoding specified.
Implementation
Rules within the template are backed by specific functions. These functions use the argument values explained above. When writing a template, think of a rule as a function signature. Rules show the function, in kebab case, along with their required arguments. See the following:
set-metadata:
encoding: json
metadata:
- key: "scabbard_admin_keys"
value: ["$(ADMIN_KEYS)"]
- key: "alias"
value: "$(GAMEROOM_NAME)"
This rule is associated with a function used to set the metadata
field of the
CreateCircuitBuilder
. The rule’s options allow for specifying the details
necessary for the function associated with the template rule to assign that
builder value. The scabbard_admin_keys
key’s value is [“$(ADMIN_KEYS)”]
,
which refers to the value assigned to the ADMIN_KEYS
argument inserted into a
list. Similarly, the alias
key would be assigned the value assigned to the
GAMEROOM_NAME
argument value. This example also shows the encoding
option,
which determines the encoding of the metadata being assigned to the builder.
Currently, the only supported option is JSON.
Environment Variables
The SPLINTER_CIRCUIT_TEMPLATE_PATH
environment variable can be used to specify
directories which contain template files, either while using the Splinter CLI or
developing an application. Regardless of whether SPLINTER_CIRCUIT_TEMPLATE_PATH
is set, the default directory, /usr/share/splinter/circuit-templates/
is
checked when retrieving template files. The environment variable allows for
control over the template files being used.
Multiple directories may be specified in SPLINTER_CIRCUIT_TEMPLATE_PATH
, with
paths delineated by :
. If multiple storage directories are specified, the
directories are given the precedence as determined by their position in the list
of paths. For example, set the SPLINTER_CIRCUIT_TEMPLATE_PATH
using the
following command:
$ export SPLINTER_CIRCUIT_TEMPLATE_PATH='foo:bar'
This affects the splinter-circuit-template-list
and the rest of the
splinter-circuit-template
commands. The list command will print out all YAML
files within the directories specified. However, when selecting a specific
template file in a command, unless the full path to the file is specified, the
first template file matching the name passed into the argument will be used in
the command. For example, there is a template file foo/bar.yaml
and
bar/bar.yaml
.
$ export SPLINTER_CIRCUIT_TEMPLATE_PATH='foo:bar'
$ splinter circuit template show bar
The following command will show the template found in the foo
directory,
foo/bar.yaml
. The full path to the template file must be specified if multiple
template files with the same name exist and the SPLINTER_CIRCUIT_TEMPLATE_PATH
variable is set.
Prerequisites
- Verify the file paths for circuit template files and the value, if any,
assigned to the
SPLINTER_CIRCUIT_TEMPLATE_PATH
environment variable. If this value is set, the directories specified will be searched in the order provided. Set the environment variable, or clear it to ensure the default circuit template storage directory,/usr/share/splinter/circuit-templates
, is used.
Using circuit templates in the Splinter CLI
The Splinter CLI offers the splinter-circuit-template-*
commands that can be
used to list and display further information about circuit templates. The
splinter-circuit-template-*
commands solely provide information about the
circuit templates found or specified. For example, the
splinter-circuit-template-show
command displays a specific template.
Additionally, all of the template arguments
may be displayed using the
splinter-circuit-template-arguments
command. More information about the
splinter-circuit-template-*
commands can be found in the circuit template
CLI
reference.
The template is put into action using the splinter-circuit-propose
command’s
template
option. The template
option takes the template name and then uses
the template rules and arguments to create the circuit proposal. The following
example illustrates how to use a simple circuit template to propose a circuit.
Below is an example of a simple circuit template. This template only requires
the SIGNER_PUB_KEY
argument and has rules to set the circuit’s management type
and services.
version: v1
args:
- name: NODES
required: false
description: "List of node IDs"
- name: SIGNER_PUB_KEY
required: true
description: "Public key of the signer"
rules:
set-management-type:
management-type: "simple"
create-services:
service-type: ‘simple’
first-service: ‘AA01’
service-args:
- key: 'admin_keys'
value: [$(SIGNER_PUB_KEY)]
This template can be specified using the template
option of the propose
command. The template arguments are set using the corresponding template-arg
option. If any required arguments are not set, the circuit proposal will not be
created and an error will be returned specifying the missing argument. While the
circuit template will complete the circuit proposal based on its rules and
arguments, any custom-set arguments using the propose
command’s other options
will take precedence over the default template values. Any additional information
for the circuit proposal may also be included using the command’s other options.
More information on this command can be found in the circuit propose CLI
reference.
NOTE: Template files within Splinter are stored by default in
/usr/share/splinter/circuit-templates
unless theSPLINTER_CIRCUIT_TEMPLATE_PATH
environment variable is set. If using a template file outside of the paths specified by this environment variable or the default directory, use the full path when specifying thetemplate
option to ensure the correct template file is used when proposing the circuit.
This command proposes a simple circuit with one other node using the template
option.
- The proposing node has ID alpha001 and endpoint tcps://splinterd-node-acme001:8044.
- The other node has ID beta001 and endpoint tcps://splinterd-node-beta001:8044.
$ splinter circuit propose \
--node alpha001::tcps://splinterd-node-alpha001:8044 \
--node beta001::tcps://splinterd-node-beta001:8044 \
--template simple.yaml \
--template-arg SIGNER_PUB_KEY=PRIVATE-KEY-FILE \
--url URL-of-splinterd-REST-API
If successful, this command will create a simple circuit proposal based on the
information gathered by the circuit template and the node
arguments provided.
Using circuit templates in Splinter applications
This example uses the circuit template file used in the Splinter Gameroom application. The same concepts and procedures may be applied to any type of circuit. The following example guides developers through the process of using a circuit template file in an application.
-
First the template must be loaded, usually from a YAML file, to create a
CircuitCreateTemplate
object. All existing circuit template files use the YAML format. The following line shows how a template is loaded from the circuit template directory:let template = CircuitCreateTemplate::from_yaml_file("gameroom.yaml")?;
NOTE: All available circuit templates are packaged in the default circuit template directory,
/usr/share/splinter/circuit-templates
. Unless theSPLINTER_CIRCUIT_TEMPLATE_PATH
is set, in which case all directories specified using this environment variable are searched for the template files. -
The args of the
CircuitCreateTemplate
object created in the step above must be set. Each entry in the args section of the circuit template holds further information on how the argument is used. For example, theNODES
argument is described as follows in the circuit template:name: NODES required: true description: "List of node IDs"
This entry shows that the
NODES
argument is required and a short description. Therefore, if this argument is not set in the circuit template, the circuit template will not successfully create the builder objects. Some arguments have default values, this is shown in theADMIN_KEYS
argument entry in the circuit template file.name: ADMIN_KEYS required: false default: SIGNER_PUB_KEY description: >- Public keys used to verify transactions in the scabbard service
As this argument is not required, there is a default value applied if it is not provided. This default value is the value of the
signer_pub_key
argument. For the Gameroom circuit template, thenodes
andgameroom_name
are required to be set. To set these values:template.set_argument_value("nodes", &list_of_nodes)?; template.set_argument_value("gameroom_name", gameroom_alias)?;
The
list_of_nodes
is a string with all circuit participants’ node ID separated by a comma. Similarly, thegameroom_alias
value is also a string. The other arguments available to be set for the Gameroom circuit template are thesigner_pub_key
, which is the public key of the transaction signer represented by a string, and theadmin_keys
, are the admin keys used by the Scabbard service represented by a list of strings. These values are set similarly to the required values:template.set_argument_value("signer_pub_key", signer_public_key)?; template.set_argument_value("admin_keys", admin_key_list)?;
-
Once all of the required arguments have been set, the circuit template object may be applied to builder objects that are converted to the finalized object once all of the necessary circuit information is gathered. This is done as follows:
let mut create_circuit_builder = CreateCircuitBuilder::new() .with_authorization_type(&AuthorizationType::Trust); create_circuit_builder = template .apply_to_builder(create_circuit_builder)?;
The
create_circuit_builder
is aCreateCircuitBuilder
which is used to compile the necessary information to propose a circuit.At this point, all of the rules are applied to the circuit template. This means that the builder is assigned values based on the rules using the values from the arguments. In the gameroom circuit template, this includes the
create-services
,set-management-type
, andset-metadata
rules. rules are predefined functions that use the values set for the template’s args that produce information necessary to fill in the blanks of the builder objects. The full functions used for these rules can be found within thecircuit::template::rules
module. The template entry for each rule also gives some information as to what the rule is actually doing.For example, the
set-metadata
rule sets themetadata
field of theCreateCircuitBuilder
. From the template:set-metadata: encoding: json metadata: - key: "scabbard_admin_keys" value: ["$(ADMIN_KEYS)"] - key: "alias" value: "$(GAMEROOM_NAME)"
From the template entry, we can see the encoding for the
metadata
is JSON. The value of themetadata
field is represented by a map, a set of keys and values, as shown in the template. One key isscabbard_admin_keys
which takes the value of theadmin_keys
template argument. Thegameroom_name
argument is assigned to thealias
key.The
create_services
rule generates aSplinterServiceBuilder
for each service involved in the circuit. For a circuit with two participating nodes, this rule would generate twoSplinterServiceBuilder
objects. A different type of service, besides Scabbard, may also be added to the circuit template YAML file by adding an entry under thecreate_services
rule. Thecreate_services
rule on the Gameroom template generates ScabbardSplinterServiceBuilder
objects, asscabbard
is specified for theservice-type
. Similar to theset-metadata
rule, this rule also creates a map to hold theservice-args
used to initialize the Scabbard instance.This rule includes the
admin_keys
in theservice-args
entry and apeer_services
value. Thepeer_services
key has a value,$(ALL_OTHER_SERVICES)
, meaning this key is assigned the definition of thepeer_services
key.create-services: service-type: 'scabbard' service-args: - key: 'admin_keys' value: [$(ADMIN_KEYS)] - key: 'peer_services' value: '$(ALL_OTHER_SERVICES)' first-service: 'a000'
The list of
SplinterServiceBuilder
are then built and the resultingSplinterServices
are added to theCreateCircuitBuilder
’sroster
field -
After the builder has been updated for the
CircuitCreateTemplate
, any remaining information may be added to any of the builders. The Gameroom example includes filling in themembers
field of theCreateCircuitBuilder
.Once a list of the members has been created, represented by a list of
SplinterNode
structs. ASplinterNode
has anode_id
field and anendpoints
field. Assuming the list ofSplinterNode
objects has been created, and is calledmembers
, setting this value in the builder looks like the following line:create_circuit_builder.with_members(members);
-
At this point, all necessary information has been added to the
CreateCircuitBuilder
and it is ready to be turned into theCreateCircuit
message.let create_circuit = create_circuit_builder.build()?;
This will result in a CreateCircuit
object which holds all of the information
submitted to the circuit template and the builder objects.