Skip to content

Commit

Permalink
WIP: Documentation and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
cliffhall committed Feb 11, 2022
1 parent 244d33a commit dfe2b57
Show file tree
Hide file tree
Showing 17 changed files with 537 additions and 110 deletions.
142 changes: 43 additions & 99 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,100 +1,44 @@
# Fismo
## Multiuser Finite State Machines (FSMs) for Ethereum

### What is an FSM?

- A finite number of _States_, in a directed graph.
- The edges connecting one State to another is a _Transition_.
- Transitions between States are initiated by _Actions_.
- Transitions can also have associated _Guard_ logic, triggered on exit or entry to a State.

#### What can they usefully map to?

As a software pattern, they are frequently used to map processes. A couple of common examples:
* Software installation wizards; a series of forms or choices with validation rules controlling progress.
* The myriad steps in a commercial home loan process, each of which has rules about moving to the next.

But practically anything that can be described with a directed graph could be modeled as an FSM. States can represent...
* Rooms in an dungeon, which an adventurer wanders through, being met with challenges.
* Positions in a tournament bracket,
* A player's health, such as in hungry state, eating is required before moving to another state.
* An object such as a door, which could be opened, closed, or locked

#### Example
![Lockable Door FSM example](docs/LockableDoorFSM.png)

### How is Fismo different from other FSM implementations?
Most FSM implementations are set up to track state for a single machine.

For instance, even though multiple people may interact with an FSM-based auction contract, it is the current state of
the auction that's being tracked (pending, open, closed). What the users are allowed to do is based on the state of
the machine. The user has no "state" to speak of.

Fismo flips all this on it's head. It tracks the state of _each user in every machine they interact with_.

A Machine is a combination of static configuration and guard logic that governs the current state of a user on that machine.

One part is purely configuration...
* A graph of _states_
* Valid _transitions_ between states
* The _actions_ that can trigger _transitions_

The other, much more interesting part is "guard code"...
* Each state can have an entrance guard; a function that will be called when a user enters a state
* Each state can have an exit guard; a function that will be called when a user exits a state
* Passing out of one state and into another, then, could trigger zero through two functions
* Guard code can do anything, but is so-called because it is a chance to revert the transaction
- _An adventurer is chained to a post in the middle of the room_
- They were chained there by a troll when they came into the room. (This happened in the entrance guard logic.)
- The exit guard for the room can revert if the user's state for the machine representing the chain is "shackled".
- Some action a user takes in that machine could switch them to the "unshackled" state.
- Once unshackled, the user can attempt the transition to the other room again.
- Now the exit guard lets the user pass, and _maybe_ the entrance guard to the next room does as well - who knows?

#### What's in a machine configuration?
* Any number of discrete states.
* The valid transitions to other states and the actions that trigger them.
* Whether proxied guard logic exists for exiting a state.
* Whether proxied guard logic exists for entering a state.
* An off-chain metadata URI for each machine that describes this configuration in JSON format.
- Like NFT metadata, commonly stored on IPFS.
- Can have longer descriptions than is financially feasible for casual building on-chain.
* Eventually, an on-chain metadata function that returns a bare-bones JSON description of the machine structure:
- Is possible (see the AvastarsMetadata contract)
- But no machine, state, transition, or action descriptions other than their names and ids would be included.
- Could be used to construct client side text or multimedia representations of the machine in question.

![Lockable Door FSM example](docs/LockableDoorConfig.png)

#### Fismo.sol, what does it do?
It is a combination of...
- the Proxy pattern for executing guard logic in the context of the Fismo contract
- a registry of machines and their users' states within them
- and orchestrator of all state transitions in all machines for all users

It maintains...
* Configurations for any number of named machines
* Current state in any number of machines for any number of wallet addresses
* A history of each user's position (current machine and state)

It executes...
* Actions that trigger user transitions between states
* State-specific entrance and exit guard logic by delegating to logic contracts
- This is similar to the facets of the [Diamond proxy pattern](https://eips.ethereum.org/EIPS/eip-2535).
- Except, function signatures on Fismo's proxied logic contracts are...
- Deterministic. Based on the machine and the states involved in a transition.
- Example: `MachineName_StateName_Enter(address _user)` or `MachineName_StateName_Exit(address _user)`.
- Simpler to maintain while avoiding name collisions.

It emits events when...
* A user's state changed in some machine
* A user is about to exit a state
* A user is about to enter a state
* A machine is created
* A machine is modified
* A state's guard logic contract is changed

It reverts if...
* A configured entrance guard returns false
* A configured exit guard returns false
* Machine is misconfigured in some way
## Lab 🧪 [Setup](docs/setup.md) 🧪 [Tasks](docs/tasks.md) 🧪 [FAQ](docs/faq.md) 🧪 [About](docs/about.md)

## Multitenant Finite State Machines for Ethereum
#### An experiment in Deterministic Proxy design

## Status
Currently in development.

Done or in progress are:
- ✅ Developer setup and tasks documentation
- ✅ Developer environment configuration template
- ✅ Working [Deterministic Proxy](docs/about.md#a-deterministic-proxy-experiment) implementation
- ✅ Deployable examples
- ✅ Clear and complete inline [NatSpec](https://docs.soliditylang.org/en/v0.8.11/natspec-format.html?highlight=NatSpec) docs and in-method comments
- ✅ Separation of concerns into inheritance tree for easy comprehension and maintenance
- ✅ Shared domain model for contract structures, enums, events, & constants
- ✅ Domain model expressed in JS
- ✅ Domain model unit tests
- ✅ Script modules for reuse in both deployment and testing
- ✅ Contract unit tests
- 👉 High level architecture documentation
- 👉 Project structure documentation
- 👉 API documentation
- 👉 Machine operation tests (multi-step operation of machine examples)
- 👉 Explore minimal clones for cheap deployments.

## Developer Docs
- [Setup](docs/setup.md) - Get going quickly on your local system.
- [Tasks](docs/tasks.md) - Build, test, analyse, and deploy.
- [FAQ](docs/faq.md) - Frequently asked questions.
- [About](docs/about.md) - What is this project even about?

## License
* Fismo - Copyright © 2021-2022 Futurescale, Inc.
* License BSD-3-Clause

* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of Futurescale, Inc., Fismo, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
7 changes: 3 additions & 4 deletions contracts/components/FismoOperate.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ pragma solidity ^0.8.0;

import { FismoUpdate } from "./FismoUpdate.sol";
import { IFismoOperate } from "../interface/IFismoOperate.sol";
import { console } from "hardhat/console.sol";

/**
* @title FismoOperate
Expand Down Expand Up @@ -140,11 +139,11 @@ contract FismoOperate is IFismoOperate, FismoUpdate {
}
}

// Return the guard message
guardResponse = string(response);
// Decode the response message
(guardResponse) = abi.decode(response, (string));

// Revert with guard message as reason if invocation not successful
//require(success, guardResponse);
require(success, guardResponse);

}

Expand Down
5 changes: 2 additions & 3 deletions contracts/example/LockableDoor/LockableDoorGuards.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { console } from "hardhat/console.sol";

/**
* @notice Transition guard functions
Expand All @@ -19,14 +18,14 @@ contract LockableDoorGuards {
function LockableDoor_Locked_Exit(address _user, string calldata _nextStateName)
public
view
returns(string memory message)
returns(string memory)
{
// User must have key to unlock door
bool hasKey = true; // TODO: check if user holds NFT representing key
require(hasKey, "User must hold key to unlock.");

// Success response message
message = "Door unlocked.";
return "Door unlocked.";

}

Expand Down
2 changes: 1 addition & 1 deletion contracts/mock/MockFismo.sol
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ contract MockFismo is FismoOperate {
returns (string memory guardResponse)
{

return invokeGuard(_user, _machineName, _stateName, _guard);
guardResponse = invokeGuard(_user, _machineName, _stateName, _guard);

}

Expand Down
41 changes: 41 additions & 0 deletions docs/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Fismo
## [Lab](../README.md) 🧪 [Setup](setup.md) 🧪 [Tasks](tasks.md) 🧪 [FAQ](faq.md) 🧪 About

## A Deterministic Proxy Experiment
Most extensible among Solidity contract proxy patterns is the [EIP-2535](https://eips.ethereum.org/EIPS/eip-2535) Diamond Multi-Facet Proxy specification. It allows a proxy to have more than one upgradeable implementation (logic) contract. The Diamond architecture is extremely useful for almost any non-trivial contract suite, lending a modular building approach that can place any amount of logic behind a single Ethereum address. Having built upon the Diamond architecture multiple times, I highly recommend it.

However, one criticism auditors and implementers have often raised is the level of complexity involved in managing the "Facets" (how the Diamond spec refers to implementation contracts) and their function selectors. That complexity is for the most part tucked away out of sight and works flawlessly. But it does beg the question:

> What other forms might the multiple-implementation proxy pattern take?
Fismo began as an experiment in what I will call Deterministic Proxy design. The idea was that the function selectors would be determined not by calling complicated maintenance functions to associate a given function selector with its implementation, but rather by **generating the function selector on the fly** somehow, based on the execution context.

Nifty idea, but without a problem domain, this hypothetical deterministic proxy would have no context within which to formulate function selectors to be proxied.

## Problem Domain: Finite State Machines
FSMs are a perfect match for this experiment. As problem domains go, it's relatively simple, but still non-trivial. You can describe them in a lightweight way, validate their machine-readable description easily, consume it in Solidity, and place it in contract storage. It is straightforward to enforce that transitions between states follow the edges of their directed graph. Fismo does that automatically.

But there is one place where you need to add custom code: _guarding state transitions._

In the [Lockable Door](../contracts/example/LockableDoor) example, going from the Locked state to the Unlocked state should require that the user have a key. That could be an NFT, or just some state in the contract.

This is where you need to write some code and deploy a guard logic implementation contract. It is also where we get a chance to test the Deterministic Proxy hypothesis at the heart of the Fismo experiment.

In the machine definition for the Locked state, the `enterGuarded` flag should be set to `true` and its `guardLogic` property set to this contract address. When Fismo tries to transition between the Locked state and the Open state, it will attempt to execute the following function by combining machine name, state name, and guard direction.

> `LockableDoor_Locked_Exit(address user, string memory _nextStateName)`
This requires a developer to write function signatures in a very specific way, but it nicely demonstrates the Deterministic Proxy concept with no chance of function signature collision, since:
- Each machine name must be unique
- Within a machine, each state name must be unique
- There are only two valid guard directions

Additionally, we end up with a nice protocol that can be used to simulate, oh, I don't know...
- [Nearly any describable process](https://scholar.google.com/scholar?q=process+simulation+with+finite+state+machines&hl=en&as_sdt=0&as_vis=1&oi=scholart)
- [Chatbots or non-player characters in games](https://www.hamidadelyar.com/blog/finite-state-machine-chatbot/)
- [Software architecture](328717831_Modeling_Software_with_Finite_State_Machines_A_Practical_Approach)
- [The behavior of living creatures](https://mind-simulation.com/en/blog/tech/using-finite-state-machines-to-model-behavior.html)
- [Adventure game worlds with blockchain-native assets](https://www.mecs-press.org/ijieeb/ijieeb-v13-n4/IJIEEB-V13-N4-5.pdf)

... just to name a few. What will you build?

Binary file removed docs/cave.jpeg
Binary file not shown.
Loading

0 comments on commit dfe2b57

Please sign in to comment.