设计
pragma solidity ^0.8.0;
abstract contract StateMachine {
event Transition(address sender, bytes32 fromState, bytes32 toState);
struct State {
bool ifCreated;
mapping(bytes4 => bool) registedFunctions;
bytes32[] registedRoles;
function(bytes32, bytes32) internal view[] preFunctions;
function(bytes32, bytes32) internal[] postFunctions;
function(bytes32, bytes32) internal[] actionFunctions;
bytes32[] nextStates;
}
struct StateTransition {
bytes32 fromState;
bytes32 toState;
address transitor;
uint256 timestamp;
uint256 blockNum;
address transitorOrigin;
}
StateTransition[] public history;
mapping(bytes32 => State) internal states;
bytes32[] internal registedStates;
bytes32 internal currentState;
bytes4[] internal knownSelectors;
mapping(bytes4 => bool) internal knownSelector;
modifier checkStateMachineSetup {
require(registedStates.length > 0, 'the state machine has not been setup');
_;
}
modifier checkAllowedFunction {
require(states[currentState].registedFunctions[msg.sig], 'the function is not allowed run in this currentState');
_;
}
modifier checkTransitionIfLegal(bytes32 toState) {
checkTransitionIfLegalByCurrentState(getCurrentState(), toState);
_;
}
modifier doesStateExist(bytes32 state) {
require(states[state].ifCreated, 'this state is not created');
_;
}
function getHistoryLength() public view returns (uint256) {
return history.length;
}
function getHistory(uint256 index) public view
returns (
bytes32 fromState,
bytes32 toState,
address transitor,
uint256 timestamp,
uint256 blockNum,
address transitorOrigin
)
{
return (history[index].fromState, history[index].toState, history[index].transitor, history[index].timestamp,history[index].blockNum,history[index].transitorOrigin);
}
function getCurrentState() public view returns (bytes32 state) {
return currentState;
}
function getAllStates() public view returns (bytes32[] memory allStates) {
return registedStates;
}
function getNextStates() public view returns (bytes32[] memory nextStates) {
return states[currentState].nextStates;
}
function getState(bytes32 state) public view
returns (
bytes32 name,
bytes32[] memory nextStates,
bytes32[] memory registedRoles,
bytes4[] memory registedFunctions
)
{
State storage s = states[state];
uint8 counter = 0;
bytes4[] memory tmp = new bytes4[](knownSelectors.length);
for (uint256 i = 0; i < knownSelectors.length; i++) {
if (states[state].registedFunctions[knownSelectors[i]]) {
tmp[counter] = knownSelectors[i];
counter += 1;
}
}
bytes4[] memory selectors = new bytes4[](counter);
for (uint256 j = 0; j < counter; j++) {
selectors[j] = tmp[j];
}
return (state, s.nextStates, s.registedRoles, selectors);
}
function transitionState(bytes32 toState) public checkStateMachineSetup() checkTransitionIfLegal(toState) {
bytes32 oldState = currentState;
currentState = toState;
function(bytes32, bytes32) internal[] storage postFunctions = states[toState].postFunctions;
for (uint256 i = 0; i < postFunctions.length; i++) {
postFunctions[i](oldState, toState);
}
history.push(
StateTransition({
fromState: oldState,
toState: toState,
transitor: msg.sender,
timestamp: block.timestamp,
blockNum:block.number,
transitorOrigin:tx.origin
})
);
emit Transition(msg.sender, oldState, currentState);
}
function createState(bytes32 stateName) internal {
require(!states[stateName].ifCreated, 'this state has already been created');
states[stateName].ifCreated = true;
registedStates.push(stateName);
}
function addRoleForState(bytes32 state, bytes32 role) internal doesStateExist(state) {
states[state].registedRoles.push(role);
}
function addAllowedFunctionForState(bytes32 state, bytes4 allowedFunction) internal doesStateExist(state) {
if (!knownSelector[allowedFunction]) {
knownSelector[allowedFunction] = true;
knownSelectors.push(allowedFunction);
}
states[state].registedFunctions[allowedFunction] = true;
}
function addNextStateForState(bytes32 state, bytes32 nextState) internal doesStateExist(state) doesStateExist(nextState) {
states[state].nextStates.push(nextState);
}
function addPostFunctionsForState(bytes32 state, function(bytes32, bytes32) internal postFunction) internal doesStateExist(state) {
states[state].postFunctions.push(postFunction);
}
function addPreFunctionsForState(bytes32 state, function(bytes32, bytes32) internal view preFunction) internal doesStateExist(state) {
states[state].preFunctions.push(preFunction);
}
function setActionFunctionForState(bytes32 state, function(bytes32, bytes32) internal actionFunction) internal doesStateExist(state) {
states[state].actionFunctions.push(actionFunction);
}
function removePreFunctionForStateByIndex(bytes32 state,uint index) internal doesStateExist(state) returns (function(bytes32, bytes32) internal view[] memory){
function(bytes32, bytes32) internal view[] storage functions = states[state].preFunctions;
if(index >= functions.length) return functions;
for(uint i = index; i<functions.length-1;i++) {
functions[i]=functions[i+1];
}
delete functions[functions.length-1];
return functions;
}
function removePostFunctionForStateByIndex(bytes32 state,uint index) internal doesStateExist(state) returns (function(bytes32, bytes32) internal[] memory){
function(bytes32, bytes32) internal [] storage functions = states[state].postFunctions;
if(index >= functions.length) return functions;
for(uint i = index; i<functions.length-1;i++) {
functions[i]=functions[i+1];
}
delete functions[functions.length-1];
return functions;
}
function setInitialState(bytes32 initialState) internal {
require(states[initialState].ifCreated, '');
require(currentState == 0, '');
currentState = initialState;
}
function checkTransitionIfLegalByCurrentState(bytes32 fromState, bytes32 toState) private view {
require(states[fromState].ifCreated, 'from state not been created');
require(states[toState].ifCreated, 'to state not been created');
require(checkNextStates(fromState, toState), 'from state is not linked to to state');
require(checkRegistedRoles(toState), 'cureent role has no right to transition state');
checkPreFunctions(fromState, toState);
}
function checkNextStates(bytes32 fromState, bytes32 toState) internal view returns (bool hasNextState) {
hasNextState = false;
bytes32[] storage nextStates = states[fromState].nextStates;
for (uint256 i = 0; i < nextStates.length; i++) {
if (keccak256(abi.encodePacked(nextStates[i])) == keccak256(abi.encodePacked(toState))) {
hasNextState = true;
break;
}
}
}
function checkPreFunctions(bytes32 fromState, bytes32 toState) private view {
function(bytes32, bytes32) internal view[] storage preFunctions = states[toState].preFunctions;
for (uint256 i = 0; i < preFunctions.length; i++) {
preFunctions[i](fromState, toState);
}
}
function checkPreFunctionsAndAction(bytes32 fromState, bytes32 toState) private {
function(bytes32, bytes32) internal view[] storage preFunctions = states[toState].preFunctions;
for (uint256 i = 0; i < preFunctions.length; i++) {
preFunctions[i](fromState, toState);
}
function(bytes32, bytes32) internal [] storage actionFunctions = states[toState].actionFunctions;
for (uint256 i = 0; i < actionFunctions.length; i++) {
actionFunctions[i](fromState, toState);
}
}
function checkRegistedRoles(bytes32 toState) private view returns (bool isAllowed) {
isAllowed = false;
bytes32[] storage registedRoles = states[toState].registedRoles;
if (registedRoles.length == 0) {
isAllowed = true;
}
}
function setupStateMachine() internal virtual;
}