solidity-ide icon indicating copy to clipboard operation
solidity-ide copied to clipboard

Codegenerator - Layout of generated Solidity code

Open flantony opened this issue 6 years ago • 5 comments

The simple goal of the statechart integration is to empower non-programmers to create solidity smart contracts.

In order to achieve this we will integrate the open source tool YAKINDU Statechart Tools , which provides a graphical editor to create finite state machines and generate code from it. The tool has proven its abilities in education, various industries and even in safety critical environments where human lives are at stake.

diagramtocode

We started implementing a code generator to automatically create Solidity code from a model.

The goal for the code generator is to produce code that is "optimal" in regards to:

  • gas consumption
  • prevention of known attack vectors
  • means to be updated

While being experienced in building tools and code generators, we are relatively new to Solidity and therefore need your help!

Any hint to pitfalls, best practices, tips & tricks, reviews, discussions or even code contributions are more than welcome!

flantony avatar Sep 06 '18 10:09 flantony

There are basically 3 ways to implement a state machine in code:

YAKINDU Statecharts uses the switch case approach since it is a good compromise between readability and execution speed. But maybe we should check which of the implementation pattern is best in relation to gas consumption.

andreasmuelder avatar Sep 06 '18 11:09 andreasmuelder

The solidity documentation contains an example that uses modifiers to implement state transition logic. For the minimalistic example given this makes sense, but i am not sure if the statemachiene gets bigger and has additional guard conditions.

flantony avatar Sep 06 '18 12:09 flantony

Here is an simple example of the current state:

simplesctexample

This will currently (beta not ready to work with) lead to following code:

pragma solidity ^0.4.18;
contract SimpleExample {
     enum States {
		SimpleExample_main_region_StateA,
		SimpleExample_main_region_StateB,
		SimpleExample_main_region_StateC
	}
		
	Events private lastEvent;
		
	enum Events {
		A,
		B,
		C
	}
		
	// This is the current state.
	States public activeState = States.SimpleExample_main_region_StateA;
	
	// Owner of the contract
	address private owner; 
	// Owner of the contract
	uint private lastInteraction; 
			
	// Internal scope
		
	modifier react() {
		_;
		if(activeState == States.SimpleExample_main_region_StateA){
			if ((Events.B == lastEvent)) {
				// TODO implement state reactions
			}
		}
		if(activeState == States.SimpleExample_main_region_StateB){
			if ((Events.C == lastEvent)) {
				// TODO implement state reactions
			}
		}
		if(activeState == States.SimpleExample_main_region_StateC){
			if ((Events.A == lastEvent)) {
				// TODO implement state reactions
			}
		}
	}
		
	// constructor
	function SimpleExampleStatemachine()public {
		owner = msg.sender;
	}
		
	// default function
	function() public payable {}			
		
	// Scope
	function A() public react {
		lastInteraction = block.timestamp;
		lastEvent = Events.A;
	}	
		
	function B() public react {
		lastInteraction = block.timestamp;
		lastEvent = Events.B;
	}	
		
	function C() public react {
		lastInteraction = block.timestamp;
		lastEvent = Events.C;
	}	
		
}

flantony avatar Sep 06 '18 13:09 flantony

Global variables aka the contract's state is stored on the blockchain and a very expensive operation. Is there a reason to persist the lastEvent or could it be passed in as argument to the modifier like this:

modifier react(Events lastEvent){
    _;
    if(activeState == States.SimpleExample_main_region_StateA){
        if ((Events.B == lastEvent)) {
            // TODO implement state reactions
        }}
    if(activeState == States.SimpleExample_main_region_StateB){
        if ((Events.C == lastEvent)) {
            // TODO implement state reactions
        }}
    if(activeState == States.SimpleExample_main_region_StateC){
        if ((Events.A == lastEvent)) {
            // TODO implement state reactions
        }}
}
function A() public react(Events.A){
    lastInteraction = block.timestamp;
}
function B() public react(Events.B){
    lastInteraction = block.timestamp;
}
function C() public react(Events.C){
    lastInteraction = block.timestamp;
}

andreasmuelder avatar Sep 07 '18 11:09 andreasmuelder

This looks reasonable to me. I am wondering how to implement guads when sticking to this pattern. I could imagine to just negate the guard condition and throw an exception in case it's true. Then generate the reactions (exit, entry) into the functions?

flantony avatar Sep 07 '18 18:09 flantony