Workflow
Name | Value |
---|---|
Repository | https://github.com/anagolay/anagolay-chain/tree/main/pallets/workflows |
Pallet | Yes |
Workflow
Automatic execution of the Operations is essential to generate the Proof and for its verification and validation. Given the standard implementation and definition of a single Operation, the same input must yield the same output, no matter where the code is executed. This way we are making sure that the Statements can be verified in the future and generated only by the rightful owner and signer. Knowing nothing about the data involved, only the author can generate 100% of the same Statement, which inherently states the validity of the Proofs without exposing anything to the public. To accomplish this there are only two requirements. One is the Workflow ( chained Operations ) that is used to create the Proofs and the second is the data that was used to generate the Statement and its Proof. This can be known to the public.
A Workflow is composed of Segments, which can be thought of as parts of the Workflow that require other specific parts of the Workflow to be executed beforehand, or otherwise, parts that are in need of some external input and the Workflow execution is paused while waiting for such input.
Segmentā
A Segment definition contains a sequence of Operations, the eventual configuration of each one of them, and a reference to the input required to bootstrap the process. In fact, the required input may come from other Segments of the Workflow or from external input as well (eg: end-user interaction)
At Segment execution, each Operation of the sequence is executed in order. The previous execution result is passed on to the next execution input, and so on until there are no more Operations to execute in the Segment or a non-recoverable error occurs. Therefore, Operations in a Segment are executed in a chain, which brings the following desirable properties:
- chaining leads to the production of complex functionalities. As there is no ambiguity in the order of execution in a chain, this approach guarantees the separation of responsibility and reusability and it's preferable to the production of very large monolithic Operations
- every Operation implementation feels familiar and follows the same template which makes the developer more productive and more focused on the business logic and not on the signatures
Workflow manifestā
As an example, let's consider the Proof of Camera and Lens Ownership (PoCLO) Workflow manifest. As all Workflow manifests it contains the following information:
id
: the unique identifier of the Workflow, a hash of the following datadata
: the structure payload, namely:name
: human-readable name of the Workflowcreators
: Identifier of the creator users or system as a reference to his account id on the blockchain, pgp key or emaildescription
: human-readable description of the objective of the Workflowgroups
: Tells which groups the Workflow belongs to.PHOTO
orCAMERA
or something elsesegments
: a list of Segment definitions
Segments Example:
{
"id": "bafy2bzacedwhjyp6unu4p2ife2n5f6db4hd4jp7jr7qpati5tdgysifnlgg3w",
"data": {
"name": "Anagolay PoCLO rule",
"creators": ["did:substrate:5HnKtosumdYfHSifYKBHhNmoXvhDANCU8j8v7tc4p4pY7MMP/anagolay-network"],
"description": "Proof of Camera and Lens Ownership. Implementing this rule we can say with certainty that user owns the mentioned equipment",
"groups": ["CAMERA"],
"segments": [
{
"inputs": [
-1 // -1 means it is the user input, or external input, usually via next()
],
"sequence": [{ "version_id": "bafyxrgfddgwhp6cfkfeddwfsb45pdfadg37rl4jpdvbfdtdgysbxidfnlsdds", "config": {} }]
},
{
"inputs": [
0 // take an input from the segments[0] index
],
"sequence": [{ "version_id": "bafyfadg37rgwhpf4jpdxrgfddnlsddsfkfe6cdwdgysbxidffsb45pdvbddtl", "config": {} }]
},
{
"inputs": [-1, 1],
"sequence": [{ "version_id": "bafywhpf4jpdxdg37rgsfkdfapdvbddtdgysfe6cdwgfddnlsdbxidffsb45rl", "config": {} }]
},
{
"inputs": [2, 0],
"sequence": [
{ "version_id": "bafyvbddtdgysfjpdxdg37rgsfddnlswhpf4xidffsbkdfapddbe6cdwgf45rl", "config": {} },
{ "version_id": "bafynlsdbxidfdtfsb45rldxdgysfwhpf4jpdddg37rgsfkdfapdvbde6cdwgf", "config": {} }
]
},
{
"inputs": [-1, 3],
"sequence": [{ "version_id": "bafycdwgf45rlgysff4xidffsbkdfapddbe6gsfddjpdxdg37rvbddtdnlswhp", "config": {} }]
},
{
"inputs": [4, 3],
"sequence": [{ "version_id": "bafydffsg37rvbkdfapddbe6gsfddjpdxdcbdwgf45rlgysff4xiddtdnlswhp", "config": {} }]
}
]
}
}
The above Workflow manifest declares six Segments, the topmost (which has index 0) will be chained for execution first, and then on until the bottom one which is executed last (at index 5). Each of the six Segment definitions contains the following information:
inputs
: determines where to get the inputs for the first Operation of the Segment. According to the count of inputs required, there will be a number of (zero-based) indexes of the preceding segments that produced the required input; a value of -1 identifies the case where input is external (eg: end-user interaction)sequence
: a sequence of operation definitions with their respectiveversion_id
andconfig
.
There are additional fields that may appear in the sequence
in special cases, analogous to an override of the Operation manifest data, typically for Flow Operations:
inputs
: provides the types (and cardinality) of the inputsoutput
: provides the type of the output
The above Workflow Segments are also representable by the following flow chart:
Execution of a Segmentā
When the Operations of a Segment inside a Workflow are executed the following conditions apply:
- Each execution takes a collection of inputs and a map of configuration parameters. While the input is propagated in the chain of Operations, the configuration is static information and comes from the Workflow manifest
- The execution always returns a single output. It can be passed as input for subsequent execution or can be a final result. This implies that all Operations in the segment (except, perhaps, the first) have one input and one output.
- Inputs and outputs are (de)serializable (from)to a javascript byte array
To sum up, the automatic execution of a Segment is implemented in the Workflow as follows:
- The next Segment definition is evaluated along with some input. The input can come from the user or from other Segments. This means that Segment execution is memoized and the same Segment is executed only once.
- The Segment Operations are evaluated in sequence: the next Operation business logic is executed.
- The business logic produces a new output. At this point, if there are other Operations to chain the flow continues from 2. Otherwise, flow continues from 4.
- The Segment execution terminates returning the last output. This output, the result of the segment, is either the result that the Workflow generates or an intermediary result to pass on to further Segment execution
Execution of the Workflowā
The Workflow code is generated and compiled by the publisher service starting from its manifest data. The generated code includes all the Operations called in the correct sequence and propagates the correct input toward each of the Segments the manifest defines. Therefore, the code provides two methods that are bound to WASM:
new()
: instances the Workflownext()
: call the execution of the next Segment. It accepts as a parameter a collection of inputs that represent the external inputs for that Segment. To be clear, the caller must provide only the inputs that are indicated as -1 in the manifest, since the inputs coming from other Segments are automatically provided.
The result of a call to next()
is a structure containing the following fields:
done
: boolean that indicates if the Workflow execution has been completedresult
: only available in the last Segment execution, whendone
is true, since producing a result every call tonext()
incurs in a performance penaltysegment_time
: performance measurement of the time taken to execute the segmenttotal_time
: performance measurement of the time taken to execute the Workflow up to the current Segment