Skip to content

Instantly share code, notes, and snippets.

@stackdump
Created May 25, 2023 15:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stackdump/ed25acd9405d3fa5ceca46064b025a7c to your computer and use it in GitHub Desktop.
Save stackdump/ed25acd9405d3fa5ceca46064b025a7c to your computer and use it in GitHub Desktop.
metamodel POC (WIP)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
// Uncomment this line to use console.log
// import "hardhat/console.sol";
// see also: bitwrap.io: where we are developing a generalized state channel for web3
contract Metamodel {
address payable public owner;
Place[] internal places;
Transition[] internal transitions;
event Action(uint8 txnId, uint8 multiple, uint when);
struct PetriNet {
Place[] places;
Transition[] transitions;
int8[] initialState;
}
struct Transition {
uint8 offset;
uint8 role;
uint8 action;
int8[] guard;
int8[] delta;
}
struct Place {
uint8 offset;
int8 initial;
int8 capacity;
}
struct Response {
int8[] output;
uint8 action;
uint8 role;
uint8 multiple;
bool ok;
}
function cell(int8 initial, int8 capacity) public returns (Place memory) {
Place memory p = Place(uint8(places.length), initial, capacity);
places.push(p);
return p;
}
function fn(uint8 vectorSize, uint8 action, uint8 role) public returns (Transition memory) {
Transition memory t = Transition(uint8(transitions.length), action, role, new int8[](vectorSize), new int8[](vectorSize));
transitions.push(t);
return t;
}
function txn(uint8 weight, Place memory p, Transition memory t) public {
transitions[t.offset].delta[p.offset] = int8(0-weight);
}
function txn(uint8 weight, Transition memory t, Place memory p) public {
transitions[t.offset].delta[p.offset] = int8(weight);
}
function guard(uint8 weight, Place memory p, Transition memory t) public {
transitions[t.offset].guard[p.offset] = int8(0-weight);
}
}
contract TicTacToeModel is Metamodel {
enum Role{ X, O }
// Board is a 3x3 matrix
// used to index into state vector
//
// 00 01 02
// 10 11 12
// 20 21 22
enum Board {
_00, _01, _02,
_10, _11, _12,
_20, _21, _22,
SIZE // board upper bound
}
enum Actions {
X00, X01, X02, X10, X11, X12, X20, X21, X22,
O00, O01, O02, O10, O11, O12, O20, O21, O22,
HALT // action upper bound
}
constructor() {
owner = payable(msg.sender); // REVIEW: do we really need this?
}
function addMove(Board mark, Actions action, Role role) internal {
txn(1, places[uint8(mark)], fn(uint8(Board.SIZE), uint8(action), uint8(role)));
}
function invalidAction(uint8 action ) public pure returns (bool) {
return action >= uint8(Actions.HALT);
}
function declaration() public returns (PetriNet memory) {
int8[] memory initialState = new int8[](uint8(Board.SIZE));
cell(1, 1);
cell(1, 1);
cell(1, 1);
cell(1, 1);
cell(1, 1);
cell(1, 1);
cell(1, 1);
cell(1, 1);
cell(1, 1);
initialState[0] = places[0].initial;
initialState[1] = places[1].initial;
initialState[2] = places[2].initial;
initialState[3] = places[3].initial;
initialState[4] = places[4].initial;
initialState[5] = places[5].initial;
initialState[6] = places[6].initial;
initialState[7] = places[7].initial;
initialState[8] = places[8].initial;
addMove(Board._00, Actions.X00, Role.X);
addMove(Board._01, Actions.X01, Role.X);
addMove(Board._02, Actions.X02, Role.X);
addMove(Board._10, Actions.X10, Role.X);
addMove(Board._11, Actions.X11, Role.X);
addMove(Board._12, Actions.X12, Role.X);
addMove(Board._20, Actions.X20, Role.X);
addMove(Board._21, Actions.X21, Role.X);
addMove(Board._22, Actions.X22, Role.X);
addMove(Board._00, Actions.O00, Role.O);
addMove(Board._01, Actions.O01, Role.O);
addMove(Board._02, Actions.O02, Role.O);
addMove(Board._10, Actions.O10, Role.O);
addMove(Board._11, Actions.O11, Role.O);
addMove(Board._12, Actions.O12, Role.O);
addMove(Board._20, Actions.O20, Role.O);
addMove(Board._21, Actions.O21, Role.O);
addMove(Board._22, Actions.O22, Role.O);
return PetriNet(places, transitions, initialState);
}
}
contract TicTacToeController {
bool public halted = false;
uint8 internal sequence = 0;
int8[] public state;
constructor() {
state = new TicTacToeModel().declaration().initialState;
}
function fire(uint8 txnId, uint8 role) public returns (Metamodel.Response memory) {
TicTacToeModel m = new TicTacToeModel();
// FIXME: cannot call function via contract type name
Metamodel.Transition memory t = m.declaration().transitions[txnId];
for (uint8 i = 0; i < t.delta.length; i++) {
state[i] += t.delta[i];
}
if (txnId != t.action) {
revert("Invalid action");
}
if (t.role != role) {
// REVIEW: should we look up the role to see if caller is allowed?
revert("Invalid role assertion");
}
// FIXME calculate output
Metamodel.Response memory res = Metamodel.Response(state, t.action, t.role, 1, true);
if (res.ok != true) {
revert("Invalid transition");
}
return res;
}
function move(uint8 action) public {
if (halted) {
revert("Game is over");
}
Metamodel.Response memory t;
if (sequence % 2 == 0) { // alternate X and O
t = fire(action, uint8(TicTacToeModel.Role.X));
} else {
t = fire(action, uint8(TicTacToeModel.Role.O));
}
if (t.ok) {
sequence++;
state = t.output;
} else {
revert("Invalid move");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment