Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Lilypad v0 is a "bridge" for running Bacalhau compute jobs via a smart contracts.
Lilypad is a ‘bridge’ to enable computation jobs from smart contracts. The aim of Lilypad v0 was to create an integration for users to call Bacalhau jobs directly from their solidity smart contracts and hence enable interactions and innovations between on-chain and off-chain compute. Lilypad v0 is a proof of concept bridge which runs off the public (free to use) Bacalhau compute network. As such, the reliability of jobs on this network are not guaranteed.
If you have a need for reliable compute based on this infrastructure - get in touch with us.
A user contract implements the LilypadCaller interface and to call a job, they make a function call to the deployed LilypadEvents contract.
This contract emits an event which the Lilypad bridge daemon listens for and then forwards on to the Bacalhau network for processing.
Once the job is complete, the results are returned back to the originating user contract from the bridge code.
See more about how Bacalhau & Lilypad are related below
📚 Reference
View Lilypad v0 references
🖼️ Examples
View Lilypad v0 examples
Lilypad v0 Quick Start
Deployed Network Details
Lilypad Smart Contracts
Creating your own Jobs
Architecture
Running a Stable Diffusion Job from a smart contract with Lilypad v0
Open this contract in Remix -> Click here
This example uses the Stable Diffusion Docker image found on the Bacalhau Docs.
For more info on how to create the Stable Diffusion Script and Docker Image see this tutorial or watch the video below.
This example can be found in the Examples folder in the Lilypad Project Github.
To run this example, you can simply deploy it to any supported network and pass in the contact address to the constructor which corresponds to your network. See Deployed Network Details.
Open this example in remix -> click here
In this example the Lilypad Stable Diffusion Job Results are returned as an IPFS v0 CID. You can use Brave browser or an IPFS gateway to see the results.
For example for a given return CID, type the following into your browser url
In Brave: ipfs://[CID]
In Other Browsers: www.w3s.link/ipfs/[CID]
or www.ipfs.io/ipfs/[CID]
A folder of the Outputs will be shown:
You can also access the image directly by navigating to
In Brave: ipfs://[CID]/outputs/image0.png
In Other Browsers: www.w3s.link/ipfs/[CID]
or www.ipfs.io/ipfs/[CID]/outputs/image0.png
Stable Diffusion
Drand Off-chain Voting
Ethereum Address Fraud Detector
Running a fraud detection job with Lilypad
Open this contract in Remix -> Click here
This example uses the Fraud Detection Docker image found on Docker Hub HackFS.
Run the Image with Bacalhau using the command bacalhau docker --network=http --domain=api.etherscan.com run hakymulla/hackfs:inference -- python predict.py --address "0x427aE6048C7d2DEd45a07Ea46F2873d0F9ddDb35"
Run Bacalhau get [Job ID] to the format of the spec needed to call runLilypadJob
.
For more info on the dataset, how to create the Fraud Detection Script and Docker Image see My HackFS Github.
This example can be found in the Examples folder in the Lilypad-v0 Project Github.
To run this example, you can simply deploy it to any supported network and pass in the contact address to the constructor which corresponds to your network. See deployed-network-details.md
.
Open this example in remix -> click here
To Deploy on Filecoin Calibration Net, use the LilypadEvents Contract Address deployed on the testnet (0xdC7612fa94F098F1d7BB40E0f4F4db8fF0bC8820).
Call the IsFraudDetector function and add an Ethereum Address as input.
IsFraudDetector is a payable function that calls the the bridge runLilypadJob.
In this example the Lilypad FraudDetection Job Results is returned as a StdOut. You can use the allResult function to see the results.
Let's build on the above example by Creating and Interacting with Tableland table using smart contracts.
Open this example in remix -> click here
During contract deployment, a tableland table is created. The bridge while calling the lilypadFulfilled also saves the results of inference to the table which can be used to monitor the model perfomance.
Storing a reference to the table ID and prefix within your contract is advisable since it enhances ease of reference in different methods, facilitating smoother execution.
The TablelandDeployments contract helps set up the TablelandTables interface by returning ITablelandTables instantiated with the correct registry address, inferred by the chain being used. This is added to the constructor. The table columns includes an id, address and the result.
A insert function is created to write to the above table
notice the onlyBridge modifier, only the bridge can write to the table and happens during the call back.
The above insert function is called in the lilypadFulfilled function
Query the table on Click here.
Creating Bacalhau Job Spec's
There are several public examples you can try out without needing to know anything about Docker or WASM specification jobs -> see the . The full specification for Bacalhau jobs can be .
Bacalhau is language-agnostic, and supports or workloads. As long as you can run your executable in a container, you can run it in Bacalhau.
You need to supply a Bacalhau job spec. To create a job spec, you can:
Run a Bacalhau job successfully, and then get the job spec back using bacalhau describe <job_id> --format=json
.
Generate a job spec without running anything, using bacalhau docker run --dry-run
.
Writing a job spec by hand, by using our as a guide.
What can I do with Bacalhau now? You can:
read from IPFS, Filecoin, or URLs
write into Estuary or IPFS
Bacalhau operates by executing jobs within containers. This means it is able to run any arbitrary Docker jobs or WASM images
Here's an example JSON job specification for a Stable Diffusion job which runs in a Docker container, requires no verification and publishes to .
Here's an example of using this JSON specification in a solidity smart contract:
Note that since we need to be able to add the user prompt input to the middle of the spec (in the Docker entrypoint), it's been split into 2 parts.
Try out
YOLO
OCR
Video Editing
and many, many more!
There is a full complement of example jobs you can leverage on the
Implementing an unbiased off-chain voting process using Lilypad and Bacalhau, with Drand Time-lock encryption
This example illustrates how DeFi Kicks leverages Lilypad, Bacalhau, and Drand to implement an unbiased off-chain voting mechanism. The purpose of using these technologies is to ensure that voting happens in an unbiased, decentralized manner, thus promoting true democracy within the DefiKicks ecosystem.
The contracts and docker images have also been added as an example in Lilypad Github
For more info on DeFi Kicks and their use of Lilypad, see the DefiKicks Github
The DeFi Kicks project utilizes a three-step mechanism to ensure an unbiased voting system. Here's a summarized outline of how it operates:
The voting process is performed off-chain to maintain efficiency and privacy. Users cast their votes by encrypting them on the front end, signing the message for authenticity, and then sending them to be stored. Time-lock encryption is utilized, making the vote only decryptable after a predetermined amount of time, which ensures fairness and protects against any attempts to tamper with the votes.
Once the voting phase for a proposal has ended, anyone can request a vote resolution. This action requires a certain fee and can only be performed if the vote is in the ResolutionToRequest state. This fee covers the usage of Lilypad and Bacalhau for off-chain vote resolution.
Once a vote resolution request has been submitted and the necessary fee paid, Bacalhau, via Lilypad, takes care of the off-chain resolution. It processes the vote data, decrypting the votes when the predetermined time has elapsed, and generates the final voting results. This off-chain resolution ensures a high level of security and accuracy, providing an unbiased outcome for the voting process.
In summary, this mechanism leverages the strengths of off-chain voting and resolution in a decentralized environment, guaranteeing privacy, fairness, and tamper resistance in the voting process, which are all critical to maintaining the trust and integrity of the DeFi Kicks project.
Vote details are packaged into a message object and signed cryptographically. This message is then encrypted using a time-lock mechanism, which ensures decryption only after a certain time. The encrypted vote, along with the user's account details and signature, are stored as a vote object. Lastly, the new vote is stored and the system is notified.
The following code snippet shows how the off-chain voting mechanism is implemented in the frontend of the DefiKicks application. The original code can be found in the DefiKicks Github
Once the off-chain voting phase has ended anyone can request it's resolution by calling the requestVoteResolution
function in the Governor smart contract. This function in turn will trigger a Lilypad job that will fetches all the votes and resolve the voting process.
The Lilypad job is defined as docker image that runs a javascript script that fetches all the votes and resolves the voting process. We will go into the details of the script in a later section.
The Lilypad job spec is dinamically built to send the proposal ID as environment variable together with a node url used to fetch the vote power of each voter. The node url is also used to fetch the proposal details from the chain.
See the original smart contract for more details.
The script run in Bacalhau perform the following steps:
Retrieve all the off-chain votes from a decentralised storage (Ceramic stream or IPFS)
Decrypt the votes using Drand timelock decryption function
Verify the signature of the decrypted vote
Compute the vote power of each voter
Compute the vote result using the vote power of each voter
Compute the inflationary rewards for each voter that voted for the winning option
Put the reward information in a Merkle tree and compute the Merkle root together with the proof for each voter
Encode all the information in a hex string and console.log it as this is the stdout that will be sent back to the Governor smart contract by the Bacalhau operators that run the job.
Here is the vote decryption and verification code, for the full script see the original script
Once the job is picked up by the Bacalhau operators and executed, the resolutions are brought on-chain and the Governor smart contract receives a lilypadFulfilled
callback from Lilypad.
The _result
string contains all the vote resolution information encoded in a hex string. The string is decoded and the resolution information is stored in the smart contract.
For more information on how the result is decoded see the original smart contract
The Lilypad v0 Smart Contracts
Create a contract that implements LilypadCallerInterface
. As part of this interface you need to implement 2 functions:
lilypadFulfilled
- a callback function that will be called when the job completes successfully
lilypadCancelled
- a callback function that will be called when the job fails
To trigger a job from your contract, you need to call the LilypadEvents
contract which the bridge is listening to. You will connect to Bacalhau network via this bridge. Create an instance of LilypadEvents
in your own contract by passing the public contract address above to the LilypadEvents
constructor. See Deployed Network Detailsfor address details
To make a call to Bacalhau, call runLilypadJob
from your function. You need to pass the following parameters:
_from
address
The address of the calling contract, to which success or failure will be passed back. You should probably use address(this) from your contract.
_spec
string
A Bacalhau job spec in JSON format. See below for more information on creating a job spec.
_resultType
The type of result that you want to be returned. If you specify CID, the result tree will come back as a retrievable IPFS CID. If you specify StdOut, StdErr or ExitCode, those raw values output from the job will be returned.
This contract is the bridge between the Bacalhau network and smart contracts & does all the heavy lifting.
Note - you cannot deploy your own version of this contract. It will not trigger Bacalhau jobs to run on its own and requires the Lilypad GO Daemon integration. You need to use the address of the deployed LilypadEvents contract within your own smart contract.
To use Lilypad, you only need to take note of one function in this events contract - the runLilypadJob(address _from, string memory _spec, uint8 _resultType)
function which takes the following parameters.\
_from
address
The address of the calling contract, to which success or failure will be passed back. You should probably use address(this) from your contract.
_spec
string
A Bacalhau job spec in JSON format. See docs for more information on creating a job spec.
_resultType
The type of result that you want to be returned - specified in the LilypadCaller Interface. If you specify CID, the result tree will come back as a retrievable IPFS CID. If you specify StdOut, StdErr or ExitCode, those raw values output from the job will be returned.
Open LilypadEvents Contract in Remix -> Click here
This interface ensures that the results of a job run on the Bacalhau network via Lilypad can be returned to the originating contract. This interface needs to be implemented by a smart contract for Lilypad to run.
Open the LilypadCaller Interface in Remix -> Click here
Lilypad v0 Integrated Networks
If you have a use case for another network - please get in touch with us!
The Lilypad Events contract - used for triggering compute jobs on Bacalhau, is currently integrated to the following networks on the address specified:
Chain Name
LilypadEvents Contract Address
RPC
ChainID
BlockExplorer
Faucet
Filecoin Calibration Net (testnet)
0xdC7612fa94F098F1d7BB40E0f4F4db8fF0bC8820
314159 (0x4cb2f)
,
Filecoin Mainnet
0xc18879C0a781DdFa0258302467687413AaD5a4E6
, ,
314 (0x13a)
, , ,
Requires Filecoin token
Mantle Testnet
0xdC7612fa94F098F1d7BB40E0f4F4db8fF0bC8820
5001 (0x1389)
Sepolia Testnet
0xdC7612fa94F098F1d7BB40E0f4F4db8fF0bC8820
, , , see
11155111 (0xaa36a7)
, ,
Polygon Mumbai
0xdC7612fa94F098F1d7BB40E0f4F4db8fF0bC8820
see
80001 (0x13881)
,
Polygon Mainnet (coming soon)
see
137 (0x89)
Requires MATIC tokens
Optimism (coming soon)
see
10(0xa)
Requires OP tokens
Arbitrum One (coming soon)
see
42161 (0xa4b1)
Requires ARB tokens
\
Get Started with Lilypad v0 - Call off-chain distributed compute from your smart contract!
Note: Since this video was released some changes have been made to the underlying code, but the process and general architecture remains the same.
The Lilypad Contracts are not currently importable via npm (though this is in progress), so to import them to you own project, you'll need to use their github links
Using Lilypad in your own solidity smart contract requires the following steps
Create a contract that implements the LilypadCaller interface.
As part of this interface you need to implement 2 functions:
lilypadFulfilled
- a callback function that will be called when the job completes successfully
lilypadCancelled
- a callback function that will be called when the job fails
Provide a public Docker Spec compatible for use on Bacalhau in JSON format to the contract.
To trigger a job from your contract, you need to call the LilypadEvents
contract which the Lilypad bridge is listening to and which connects to the Bacalhau public network. Create an instance of LilypadEvents
by passing the public contract address on the network you are using (see Deployed Network Details) to the LilypadEvents
constructor.
Call the LilypadEvents contract function runLilypadJob()
passing in the following parameters.
_from
address
The address of the calling contract, to which success or failure will be passed back. You should probably use address(this) from your contract.
_spec
string
A Bacalhau job spec in JSON format. See below for more information on creating a job spec.
_resultType
The type of result that you want to be returned. If you specify CID, the result tree will come back as a retrievable IPFS CID. If you specify StdOut, StdErr or ExitCode, those raw values output from the job will be returned.
Create a contract that implements LilypadCallerInterface
. As part of this interface you need to implement 2 functions:
lilypadFulfilled
- a callback function that will be called when the job completes successfully
lilypadCancelled
- a callback function that will be called when the job fails
There are several public examples you can try out without needing to know anything about Docker or WASM specification jobs -> see the Bacalhau Docs. The full specification for Bacalhau jobs can be seen here.
Bacalhau operates by executing jobs within containers. This means it is able to run any arbitrary Docker jobs or WASM images
We'll use the public Stable Diffusion Docker Container located here for this example.
Here's an example JSON job specification for the Stable Diffusion job:
Here's an example of using this JSON specification in solidity:
Note that since we need to be able to add the user prompt input to the spec, it's been split into 2 parts.
See more about how to onboard your Docker Workloads for Bacalhau, Onboard WebAssembly Workloads or Work with Custom Containers in the Bacalhau Docs.
You can do this by either passing it into your constructor or setting it as a variable
Using the LilypadEvents Instance, we can now send jobs to the Bacalhau Network via our contract using the runLilypadJob()
function.
In this example we'll use the Stable Diffusion Spec shown above in #add-a-spec-compatible-with-bacalhau
Note that calling the runLilypadJob() function requires a network fee. While the Bacalhau public Network is currently free to use, gas fees are still needed to return the results of the job performed. This is the payable fee in the contract.