Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
October 26, 2022 08:25 pm GMT

Introduction to Execution Machine (EXM) - Permanent Serverless Functions

Introduction to Execution Machine (EXM)

The code for this tutorial is located [here](https://github.com/dabit3/execution-machine

EXM is a language-agnostic serverless environment powered by Arweave, and enables developers to create permanent, serverless functions on the blockchain.

EXM eliminates the need to understand smart contracts, transactions, or blocks to leverage blockchain technology.

EXM / Arweave provide something that most traditional data stores do not - permanence. Once the data / transactions are stored, they can be retrieved and consumed forever (or as long as the network exists).

Some interesting use cases might be registries, games, CMSs, permanent data protocols, general data processing, and possibly even building a permanent NoSQL database.

Under the hood, EXM utilizes SmartWeave

Lazy Evaluation

The Smartweave (and therefore EXM) execution layer is based on what's known as "Lazy Evaluation".

In most blockchains, the computation and current state is executed by the blockchain nodes. With lazy evaluation, the burden of execution is shifted to either the client or a middle layer (like EXM) that implements a processor and conveyor that essentially re-evaluates and caches the latest state of the contract so that the client does not need to process all previous transactions in order to calculate the current state.

CLI vs SDK

You can interact with EXV either via their CLI or their SDK. In this tutorial we'll be interacting with the platform using the JavaScript SDK.

Hello World tutorial

Let's explore how to build, deploy, and test an EXM app.

In this example we'll create a basic CMS that will allow a user to create, update, and delete posts. This example will enable us to implement a basic CRUD app which is the basis for most real-world applications.

Prerequisites

To be successful with this tutorial you must have:

  1. An EXM API key. You can get one here
  2. Node.js installed on your machine. I recommend installing Node.js either via NVM or FNM
  3. Mocha installed globally (if you want to run tests)

Getting started

To get started, we'll create a new Node.js project in an empty directory:

npm init --y

Next, we'll install the dependencies we'll need for our app:

npm install @execution-machine/sdk uuid chai

Finally, update package.json to add this configuration to enable ES Modules:

"type": "module",

Basic setup

For a basic EXM app you need these basic components:

  1. Initial state for the program
  2. A handler function to manage state updates. This function is the entry point for your EXM function. For a write operation to be considered valid as well as able to change the state of the function, it needs to return an object with state property in it. (more on this later)
  3. A script to deploy the program
  4. Scripts to read and update the state of the program

Configuring EXM

To interact with the platform we'll need some basic configuration that we can set up and reuse.

First, make sure you've created an EXM API key here.

Next, create and set an environment variable either in your local config file, or from your terminal:

export EXM_PK=<your-api-key>

Now, create a new file named exm.js and add the following code:

/* exm.js */import { Exm } from '@execution-machine/sdk'const APIKEY = process.env.EXM_PKexport const exmInstance = new Exm({ token: APIKEY })

Now we'll be able to interact with EXM by importing exmInstance.

Initial State

As mentioned previously, the app we'll be creating will allow users to create, edit, and delete posts. A post will be an object with the following fields:

type Post {  id: string  title: string  content: string  author: string}

To hold the posts, we can create some data structure to hold a list of posts. I will be using an object but you could also use an array.

To set up this initial state, create a new file named state.js and add the following code:

/* state.js */export const state = {  "posts": {}}

Handler function

Now that we have the initial state defined we'll create the handler function.

Create a new file named handler.js and add the following code:

/* handler.js */export async function handle(state, action) {  const { input } = action  if (input.type === 'createPost' || input.type === 'updatePost') {    state.posts[input.post.id] = input.post  }  if (input.type === 'deletePost') {    delete state.posts[input.postId]  }  return { state }}

This function will accept three different types of actions:

createPost - Creates a new post
updatePost - Updates an existing post with new data
deletePost - Deletes a post

If the input type does not match any of these, then the state is just returned without any update.

Deploy script

Now that we have the state and the handler function defined, we can create the deploy script.

This script will deploy our function to EXM and make it available for us to start using.

Create a new file named deploy.js and add the following code:

/* deploy.js */import { ContractType } from '@execution-machine/sdk'import fs from 'fs'import { exmInstance } from './exm.js'import { state } from './state.js'const contractSource = fs.readFileSync('handler.js')const data = await exmInstance.functions.deploy(contractSource, state, ContractType.JS)console.log({ data })/* after the contract is deployed, write the function id to a local file */fs.writeFileSync('./functionId.js', `export const functionId = "${data.id}"`)

Creating a new post

Next let's look at how to make a state update.

The first thing we'll want to try to do is to create a new post. Create a new file named createPost.js and add the following code:

/* createPost.js */import { exmInstance } from './exm.js'import { functionId } from './functionId.js'import { v4 as uuid } from 'uuid'const id = uuid()/* the inputs array defines the data that will be available in the handler function as action.input */const inputs = [{  type: 'createPost',  post: {    id,    title: "Hello world",    content: "My first post",    author: "Nader Dabit"  }}]const data = await exmInstance.functions.write(functionId, inputs)console.log({ data })

As commented in the code above, the most important thing to recognize is the structure of the array of inputs, and how they correlate to the arguments received in the handler function.

In createPost.js, the type will determine the type of state update that is made in the handler, and the post is the data used in the state update.

Reading the current state

Now that we have a way to deploy the function and create a post, let's look at how to read the state.

To do so, create a file named read.js and add the following code:

/* read.js */import { exmInstance } from './exm.js'import { functionId } from './functionId.js'const data = await exmInstance.functions.read(functionId)console.log("data: ", JSON.stringify(data))

You can also read the state of any function ID by calling or visiting https://api.exm.dev/read/<function-id>

Trying it out

Now that everything is set up, let's give it a spin!

From your terminal, run the deploy script:

node deploy.js

Once the function has been deployed, you should see a new file written to the project named functionId.js.

Next, read the current state of the program:

node read.js

The return value of the state should be an empty posts object.

Now, create a new post:

node createPost.js

Then read the state of the app again:

node read.js

The return value should now include the new post that was just created.

Reminder that you can also read the state of any function ID by calling or visiting https://api.exm.dev/read/<function-id>

Updating and deleting posts

Let's now create the scripts for updating and deleting posts.

For deleting a post, create a file named deletePost.js and add the following code:

/* deletePost.js */import { exmInstance } from './exm.js'import { functionId } from './functionId.js'const inputs = [{  type: 'deletePost',  postId: process.argv[2]}];await exmInstance.functions.write(functionId, inputs)

This script takes a post ID as input from the user running the script, and uses it to call the deletePost input, passing in the postId as an argument.

To update a post, create a new file named updatePost.js and add the following code:

/* updatePost.js */import { exmInstance } from './exm.js'import { functionId } from './functionId.js'const inputs = [{  type: 'updatePost',  post: {    id: process.argv[2],    title: "Hello world V2",    content: "My updated post!",    author: "Nader Dabit"  }}]const data = await exmInstance.functions.write(functionId, inputs)console.log({ data })

Now, you should be able to update and delete posts by running these two scripts, passing in the post ID:

node deletePost.js <post-id>

Testing

EXM also comes built in with testing functions in their SDK to facilitate testing in a way that can be readable & efficient.

Let's create a test script that will check that all of our action types work as we expect.

Create a new file named test.js and add the following code:

import fs from 'fs'import { TestFunction, createWrite, FunctionType } from "@execution-machine/sdk"import { state } from './state.js'import { v4 as uuid } from 'uuid'import { expect } from 'chai'const id = uuid()const functionSource = fs.readFileSync('./handler.js')const createPost = {  type: 'createPost',  post: {    id,    title: "Hello world",    content: "My first post",    author: "Nader Dabit"  }}const updatePost = {  type: 'updatePost',  post: {    id,    title: "Hello world V2",    content: "My updated post!",    author: "Nader Dabit"  }}const deletePost = {  type: 'deletePost',  postId: id}describe("Testing EXM", function () {  it("Should create a post", async function () {    const testAttempt = await TestFunction({      functionSource,      functionType: FunctionType.JAVASCRIPT,      functionInitState: state,      writes: [createWrite(createPost)]    })    const value = testAttempt.state.posts[id]    expect(value.id).to.equal(id)    expect(value.title).to.equal("Hello world")  })  it("Should update a post", async function () {    const testAttempt = await TestFunction({      functionSource,      functionType: FunctionType.JAVASCRIPT,      functionInitState: state,      writes: [createWrite(createPost), createWrite(updatePost)]    })    const value = testAttempt.state.posts[id]    expect(value.title).to.equal("Hello world V2")  })  it("Should delete a post", async function () {    const testAttempt = await TestFunction({      functionSource,      functionType: FunctionType.JAVASCRIPT,      functionInitState: state,      writes: [createWrite(createPost), createWrite(deletePost)]    })    const value = testAttempt.state.posts    const length = Object.keys(value).length    expect(length).to.equal(0)  })})

To test, run the following command:

mocha test.js

Next steps

To learn more about EXM, check out the EXM Discord.

To learn more about the Arweave Developer Ecosystem, check out learn.arweave.dev.


Original Link: https://dev.to/dabit3/introduction-to-execution-machine-exm-permanent-serverless-functions-3fhb

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To