The Mesh Constructor DSL (domain-specific language)
makes it easy to write Workflows
for the constructor
package.
This DSL is most commonly used for writing automated Construction API
tests for the mesh-cli
.
Before reading more about the Mesh Constructor DSL, we recommend learning
about the frameworks used in the constructor
to coordinate the creation of transactions.
At a basic level, the Mesh Constructor DSL syntax looks like this:
// line comment
<workflow name>(<concurrency>){
<scenario 1 name>{
<output path> = <action type>(<input>); // another comment
},
<scenario 2 name>{
<output path 2> = <action type>(<input>);
}
}
Here is a specific example for Bitcoin
:
request_funds(1){
find_account{
currency = {
"symbol":"tBTC",
"decimals":8
};
random_account = find_balance({
"minimum_balance":{
"value": "0",
"currency": {{currency}}
},
"create_limit":1
});
},
request{
min_balance = load_env("MIN_BALANCE");
adjusted_min = {{min_balance}} + 600;
loaded_account = find_balance({
"account_identifier": {{random_account.account_identifier}},
"minimum_balance":{
"value": {{adjusted_min}},
"currency": {{currency}}
}
});
}
}
create_account(1){
create_account{
network = {"network":"Testnet3", "blockchain":"Bitcoin"};
key = generate_key({"curve_type":"secp256k1"});
account = derive({
"network_identifier": {{network}},
"public_key": {{key.public_key}}
});
save_account({
"account_identifier": {{account.account_identifier}},
"keypair": {{key}}
});
}
}
If you were to write the same thing by hand in raw syntax, it would look like:
[
{
"name": "request_funds",
"concurrency": 1,
"scenarios": [
{
"name": "find_account",
"actions": [
{
"input": "{\"symbol\":\"tBTC\", \"decimals\":8}",
"type": "set_variable",
"output_path": "currency"
},
{
"input": "{\"minimum_balance\":{\"value\": \"0\", \"currency\": {{currency}}}, \"create_limit\":1}",
"type": "find_balance",
"output_path": "random_account"
}
]
},
{
"name": "request",
"actions": [
{
"type": "load_env",
"output_path": "min_balance",
"input": "MIN_BALANCE"
},
{
"type": "math",
"ouput_path": "adjusted_min",
"input": "{\"operation\":\"addition\", \"left_value\": {{min_balance}}, \"right_value\": \"600\"}"
},
{
"input": "{\"account_identifier\": {{random_account.account_identifier}}, \"minimum_balance\":{\"value\": {{adjusted_min}}, \"currency\": {{currency}}}}",
"type": "find_balance",
"output_path": "loaded_account"
}
]
}
]
},
{
"name": "create_account",
"concurrency": 1,
"scenarios": [
{
"name": "create_account",
"actions": [
{
"input": "{\"network\":\"Testnet3\", \"blockchain\":\"Bitcoin\"}",
"type": "set_variable",
"output_path": "network"
},
{
"input": "{\"curve_type\": \"secp256k1\"}",
"type": "generate_key",
"output_path": "key"
},
{
"input": "{\"network_identifier\": {{network}}, \"public_key\": {{key.public_key}}}",
"type": "derive",
"output_path": "account"
},
{
"input": "{\"account_identifier\": {{account.account_identifier}}, \"keypair\": {{key}}}",
"type": "save_account"
}
]
}
]
}
]
Note, if you plan to run the automated Construction API tester in CI for create_account
workflow, you may wish to
provide prefunded accounts
when running the tester (otherwise you would need to manually fund generated
accounts).
Workflows
are defined using the following syntax:
<workflow name>(<concurrency>){
...
}
Note, concurrency
must be provided when defining a Workflow
and
no 2 Workflows
can have the same name.
Scenarios
are defined using the following syntax:
<workflow name>(<concurrency>){
<scenario name>{
...
}
}
Scenarios
must be defined within a Workflow
and no 2 Scenarios
in the same Worfklow
can have the same name.
It is also important to note that Workflows
containing multiple
Scenarios
should be separated by a comma:
<workflow name>(<concurrency>){
<scenario name>{
...
},
<scenario name 2>{
...
}
}
In the Mesh Constructor DSL, it is possible to invoke functions (where
the function name is an Action.Type
) but not possible to define your own
functions (yet!).
The input for all functions is a JSON blob that will be evaluated by
the Worker
. It is possible to reference other variables
in an input using the syntax {{var}}
where var
must follow
this syntax.
The Mesh Constructor DSL compiler will automatically check that referenced
variables are previously defined.
Function invocations can span multiple lines (if you "pretty print" the JSON blob) but each function call line must end with a semi-colon.
The Mesh Constructor DSL provides optional "native invocation" support for 2 Action.Types
:
math
set_variable
"Native invocation" in this case means that the caller does not need to
invoke the Action.Type
in the normal format:
<output path> = <function name>(<input>);
math
can be invoked by following the syntax:
<output path> = <left side> <operator> <right side>;
A simple addition would look like:
a = 10 + {{fee}};
Instead of:
a = math({"operation":"addition","left_side":"10","right_side":{{fee}}});
set_variable
can be invoked by following the syntax:
<output path> = <input>
A simple set would look like:
a = {"message": "hello"};
Instead of:
a = set_variable({"message": "hello"});
It is not possible to invoke a function from the input of another function. There MUST be exactly 1 function call per line.
For example, this is not allowed:
a = 1 + load_env("value");
It is possible to add new line comments of comments at the end of lines
using a double slash (//
).
The Mesh Constructor DSL should be considered ALPHA
and may
include breaking changes in later releases. If you have any ideas on how to improve
the language, please
open an issue in mesh-sdk-go
!