Rosetta Constructor DSL
The Rosetta 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 rosetta-cli
.
Before reading more about the Rosetta Constructor DSL, we recommend learning
about the frameworks used in the constructor
to coordinate the creation of transactions.
Syntax
At a basic level, the Rosetta 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>);
}
}
Basic Example
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
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
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>{
...
}
}
Functions
In the Rosetta 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 Rosetta Constructor DSL compiler will automatically check that referenced
variables are previously defined.
End Line
Function invocations can span multiple lines (if you "pretty print" the JSON
blob) but each function call line must end with a semi-colon.
Native Invocation
The Rosetta Constructor DSL provides optional "native invocation" support for 2 Action.Types
:
"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
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
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"});
Recursive Calls
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 (//
).
Status
The Rosetta 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 rosetta-sdk-go
!