Skip to content

Commit

Permalink
feat: wip working template engine
Browse files Browse the repository at this point in the history
  • Loading branch information
zcstarr committed Sep 27, 2023
1 parent 0fa1f23 commit dea64c9
Show file tree
Hide file tree
Showing 18 changed files with 1,400 additions and 179 deletions.
40 changes: 40 additions & 0 deletions contracts/TinyHops.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,30 @@ interface ModicumContract {
) external payable returns (uint256);
}

struct WorkflowEntry {
uint256 stepId;
string cmd;
string params;
uint256[] deps;
}

struct Workflow {
WorkflowEntry[][] entries;
}

contract TinyHops {
address public contractAddress;
ModicumContract remoteContractInstance;

// simple ownership mapping
mapping(uint256 => address) workflowIdToOwner;
mapping(uint256 => uint256) workflowIdToStatus;
mapping(uint256 => Workflow) workflowIdToWorkflow;
mapping(uint256 => uint256) workflowIdToBalance;

// auto increment workflow id
uint256 public workflowIdCounter;

// See the latest result.

uint256 public resultJobId1;
Expand All @@ -35,6 +55,26 @@ contract TinyHops {
contractAddress = _modicumContract;
//make a connection instance to the remote contract
remoteContractInstance = ModicumContract(_modicumContract);
workflowIdCounter = 1;
}

function startWorkflow(
Workflow memory workflow
) public payable returns (uint256) {
require(
msg.value >= 5 ether,
"Payment of at least 5 Ether per step is required maybe more"
);

workflowIdToOwner[workflowIdCounter] = msg.sender;
workflowIdToWorkflow[workflowIdCounter] = workflow;
workflowIdToBalance[workflowIdCounter] =
msg.value +
workflowIdToBalance[workflowIdCounter];

// kick off the first workflow steps

return ++workflowIdCounter;
}

function runBatchSDXL(
Expand Down
126 changes: 126 additions & 0 deletions contracts/library/LibTinyHopsTemplateResolver.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
pragma solidity ^0.8.15;

// SPDX-License-Identifier: MIT
/* This specifies the format for workflows to be stored in the contract as
a byte stream. The format allows for the representation of template variables in cmds to be run
This allows us to simply store the workflow in the contract, and then apply the variables where needed
for the command prompt arguments. This is used to transform the templated workflow into a concrete workflow
The format is as follows:
- 2-byte segment type
- 8-byte segment length
- segment data
- repeat
The segment type is either 0 or 1. 0 indicates a text segment, 1 indicates a stepId segment.
The segment length is the length of the segment data in bytes.
The segment data is either the text data or the stepId. If the sgement is the stepId a uint256 is used.
The uint64 is the index of the step in the workflow. It represents the output of that specific step.
This allows us at construction of the workflow to know which steps are dependent on which other steps.
Allow us to block the execution of a step until the step it depends on has completed.
NOTE - its probably more efficient ot simply store more data and align things in 32byte increments,
additionally using efficient copy operation with pre allocation of the result string would also
be more efficient.
example:
*/
library LibTinyHopsTemplateResolver {
function applyVariables(
bytes memory byteStream,
mapping(uint256 => string) storage replacements
) public view returns (string memory) {
string memory result = "";
uint256 ptr = 0;
uint256 startptr = 0;
uint256 endptr = 0;

uint256 byteStreamLen = byteStream.length;
assembly {
ptr := add(byteStream, 0x20)
endptr := add(ptr, byteStreamLen)
startptr := ptr
}
while (ptr < endptr) {
uint256 segmentType;
uint256 rawType;
assembly {
rawType := shr(240, mload(ptr))
segmentType := shr(240, mload(ptr))
}
ptr += 2; // Move past the 2-byte prefix

if (segmentType == 0) {
// Text segment
uint64 textLength;
assembly {
// we have to adjust the pointer to align the values
textLength := shr(192, mload(ptr))
}
ptr += 8; // Move past the 8-byte length

bytes memory textBytes = new bytes(textLength);

uint256 currentPlace = ptr - startptr;
for (uint64 i = 0; i < textLength; i++) {
textBytes[i] = byteStream[currentPlace + i];
}
ptr += textLength; // Move past the text data
result = string(abi.encodePacked(result, string(textBytes)));
} else if (segmentType == 1) {
// stepId segment
uint64 stepId;
assembly {
stepId := shr(192, mload(ptr))
}
ptr += 8; // Move past the 8-byte stepId

result = string(abi.encodePacked(result, replacements[stepId]));
}
}

return result;
}

// this allows anyone to validate a byte stream to ensure it is valid prior to a workflow call
function validateByteStream(bytes memory byteStream) public pure {
uint256 ptr = 0;
uint256 endPtr = 0;
uint256 byteStreamLen = byteStream.length;
assembly {
ptr := add(byteStream, 0x20)
endPtr := add(ptr, byteStreamLen)
}
while (ptr < endPtr) {
// Ensure there's enough data for the 2-byte prefix
require(ptr + 2 <= endPtr, "Incomplete prefix");

uint16 segmentType;
assembly {
segmentType := shr(240, mload(ptr))
}
ptr += 2; // Move past the 2-byte prefix

if (segmentType == 0) {
// Text segment
// Ensure there's enough data for the 8-byte length
require(ptr + 8 <= endPtr, "Incomplete text length");

uint64 textLength;
assembly {
textLength := shr(192, mload(ptr))
}
ptr += 8; // Move past the 8-byte length

// Ensure there's enough data for the text
require(ptr + textLength <= endPtr, "Incomplete text data");
ptr += textLength; // Move past the text data
} else if (segmentType == 1) {
// stepId segment
// Ensure there's enough data for the 8-byte stepId
require(ptr + 8 <= endPtr, "Incomplete stepId");
ptr += 8; // Move past the 8-byte stepId
} else {
revert("Invalid segment type");
}
}
}
}
34 changes: 31 additions & 3 deletions example.thops.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,40 @@
"steps": [
{
"stepDesc": "First cow say",
"stepName": "cows say",
"stepId": "1",
"stepModule": {
"cmd": "cowsay:v0.0.1",
"params": "helloworld {{stepId}}"
"params": "{{stepName:2}} helloworld"
}
},
[{
"stepDesc": "First cow say",
"stepName": "cows say",
"stepId": "2",
"stepModule": {
"cmd": "cowsay:v0.0.1",
"params": "{{stepName:2}} hello parallel world {stepId:1}"
}
},
{
"stepDesc": "First cow say",
"stepName": "cows say",
"stepId": "3",
"stepModule": {
"cmd": "cowsay:v0.0.1",
"params": "{{stepName:2}} hello parallel world {stepId:1}"
}
}],
{
"stepDesc": "last cow say",
"stepName": "lastcowsay",
"stepId": "4",
"stepModule": {
"cmd": "cowsay:v0.0.1",
"params": "{{stepName:4}} hello parallel world {stepId:2} {stepId:3}"
}
}

]
}
}

Loading

0 comments on commit dea64c9

Please sign in to comment.