Created
May 25, 2023 15:04
-
-
Save stackdump/ed25acd9405d3fa5ceca46064b025a7c to your computer and use it in GitHub Desktop.
metamodel POC (WIP)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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