Skip to content

Commit

Permalink
Merge pull request #10 from harmonica-project/dev
Browse files Browse the repository at this point in the history
Adding the frontend part into the generated product
  • Loading branch information
nicoSix authored Apr 21, 2022
2 parents 303f0f5 + 17f6176 commit f5b42a7
Show file tree
Hide file tree
Showing 121 changed files with 41,522 additions and 1,393 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
node_modules
products
.DS_Store
build
build
features
59 changes: 55 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,64 @@
# The BANCO project - Blockchain ApplicatioN Configurator

<div align="center">
<br/>
<p>
The design and implementation of a blockchain application are quite challenging. With BANCO, we propose a tool to simplify this step to the point that you can generate your own application with a few clicks! The generated product includes state-of-the-art blockchain design patterns and good practices, and its modularity will allow you to build new features over this solid code ground!
</p>
<p>
Want to try it? <a href="https://banco.nicosix.com/">Click here to access the tool</a>.
</p>
<br/>
</div>

## Summary

This repository holds the codebase of BANCO, a tool to create a blockchain product following a software product line approach.
The configurator allows the user to select its desired features for the product to build.
Each feature is present in the feature model, that describe how one feature can be combined with others.
The configurator allows the user to select the desired features for the product to build.
Each feature is present in the feature model, which describes how one feature can be combined with others.
The configurator is in charge of verifying that the configuration made by the user complies with the feature model.

After the selection of features, a generator is able to assemble a working blockchain product based on the configuration and predefined templates.

This first project iteration will allow the creation of blockchain products for on-chain traceability of assets and processes.
This is still a WiP project, at the moment there is only a small prototype to configure a smart-contract managing the participants of such a product.
This first project iteration will allow the creation of blockchain products for on-chain traceability of assets and processes. Future works will consist in extending this approach to other domains and address further aspects of software product line engineering, such as product updates when modifying the software product line.

A publication, currently in review, will be referenced here in the future. It describes in-depth the method used to build a software product line for blockchain applications (the feature model and the web platform introduced in this repository) and evaluates the relevancy of the approach by assessing the cost, the code quality, and the generalisability of the approach to other traceability applications. It also gives insights into the benefits of applying SPLE for blockchain applications.

## Repository organization

The following list enumerates the important folders of this repository.

- __/evaluation__ - contains the source code generated by BANCO to serve the evaluation part of our contribution. They are shared on this repository for reproducibility and transparency purposes, following open science principles.
- __/src__ - contains the source code of BANCO
- __/artifacts__ - contains the template application and smart contracts that are used by the generator to create new blockchain products, based on the user configuration.
- __/feature_model__ - contains the SPL feature model. Can be opened in Eclipse, if FeatureIDE is installed.
- __/contracts__ - contains the smart contracts templates.
- __/manual_generation__ - contains a script to generate a product without requiring the deployment of the web platform.

## How to start BANCO

Make sure that Node.js and NPM are installed on your computer. If it is not the case, install the [latest version of Node.js here](https://nodejs.org/en/). Then, type the following commands:

```
npm install
npm start
```

Once the application has started, you can access it locally in your browser: [localhost:3000](localhost:3000).

## Template updates and additions

If you want to modify this software product line, you have to:

1. Change the feature model in Eclipse with FeatureIDE. You can find this feature model in __/src/artifacts/feature_model__.
2. Update or add new templates in the __/src/artifacts/contracts__ folder, to reflect the changes you made in the feature model. Make sure that your changes do not conflict with existing features. To better understand the process of creating templates, please read our publication (to be added).
3. Update the __/src/lib/generator/templates.js__ and the __/src/manual_generation/templates.js__ to include your features in the generation process or modify the existing ones.
4. Execute the following script while being located in __/src__ to export your templates and feature model to the __/public__ folder of BANCO, allowing the web platform to access the templates for generation purposes:

```
node export-assets
```

5. (optional) If you wish to modify the parameters provided to existing smart contract templates, make sure to also modify the generated template web application accordingly (can be found in __src/artifacts/src__.).

We are aware that modifying the existing software product line is still a burdensome process, notably the generated frontend application. Future works will be carried out to simplify this process.
97 changes: 97 additions & 0 deletions evaluation/P1/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
{
"roles": [],
"participants": [
{
"address": "0x79277e4eb7734F5533cB9D9b15bB5c130915ADAE",
"isAdmin": true,
"roles": []
}
],
"recordsCols": [],
"stateMachine": [
{
"name": "SubmitPurchaseRequest",
"roles": [],
"participants": [
"0x79277e4eb7734F5533cB9D9b15bB5c130915ADAE"
]
},
{
"name": "ApproveRequest",
"roles": [],
"participants": [
"0x79277e4eb7734F5533cB9D9b15bB5c130915ADAE"
]
},
{
"name": "ConfirmUnavailabilityLocally",
"roles": [],
"participants": [
"0x79277e4eb7734F5533cB9D9b15bB5c130915ADAE"
]
},
{
"name": "SubmitPurchaseQuotationsIPFS",
"roles": [],
"participants": [
"0x79277e4eb7734F5533cB9D9b15bB5c130915ADAE"
]
},
{
"name": "SubmitEvaluationResult",
"roles": [],
"participants": [
"0x79277e4eb7734F5533cB9D9b15bB5c130915ADAE"
]
},
{
"name": "ApproveEvaluation",
"roles": [],
"participants": [
"0x79277e4eb7734F5533cB9D9b15bB5c130915ADAE"
]
},
{
"name": "ConfirmSparePartAvailability",
"roles": [],
"participants": [
"0x79277e4eb7734F5533cB9D9b15bB5c130915ADAE"
]
},
{
"name": "SubmitPurchaseOrder",
"roles": [],
"participants": [
"0x79277e4eb7734F5533cB9D9b15bB5c130915ADAE"
]
},
{
"name": "ApprovePurchaseOrder",
"roles": [],
"participants": [
"0x79277e4eb7734F5533cB9D9b15bB5c130915ADAE"
]
},
{
"name": "ConfirmPurchaseOrder",
"roles": [],
"participants": [
"0x79277e4eb7734F5533cB9D9b15bB5c130915ADAE"
]
},
{
"name": "AnnounceAvailabilityInInventory",
"roles": [],
"participants": [
"0x79277e4eb7734F5533cB9D9b15bB5c130915ADAE"
]
},
{
"name": "CollectRequestedSparePart",
"roles": [],
"participants": [
"0x79277e4eb7734F5533cB9D9b15bB5c130915ADAE"
]
}
]
}
1 change: 1 addition & 0 deletions evaluation/P1/config.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<configuration><feature automatic="selected" manual="undefined" name="OnChainTraceabilityApp"/><feature automatic="selected" manual="undefined" name="SmartContracts"/><feature automatic="selected" manual="undefined" name="TrackingMethod"/><feature automatic="undefined" manual="selected" name="StateMachine"/><feature automatic="undefined" manual="selected" name="AssetTracking"/><feature automatic="unselected" manual="undefined" name="TokenizedAssets"/><feature automatic="undefined" manual="selected" name="DataStructAssets"/><feature automatic="undefined" manual="unselected" name="RecordRegistration"/><feature automatic="selected" manual="undefined" name="Participants"/><feature automatic="selected" manual="undefined" name="Individuals"/><feature automatic="selected" manual="undefined" name="CreateIndividual"/><feature automatic="undefined" manual="selected" name="CreateIndividualAtSetup"/><feature automatic="undefined" manual="selected" name="CreateIndividualDynamically"/><feature automatic="undefined" manual="unselected" name="IndividualType"/><feature automatic="unselected" manual="undefined" name="Oracle"/><feature automatic="unselected" manual="undefined" name="Human"/><feature automatic="unselected" manual="undefined" name="Service"/><feature automatic="undefined" manual="selected" name="DeleteIndividual"/><feature automatic="selected" manual="undefined" name="DeleteIndividualByIndividual"/><feature automatic="unselected" manual="undefined" name="DeleteIndividualByRole"/><feature automatic="undefined" manual="unselected" name="Roles"/><feature automatic="unselected" manual="undefined" name="CreateRoleAtSetup"/><feature automatic="unselected" manual="undefined" name="RemoveRole"/><feature automatic="unselected" manual="undefined" name="AddRole"/><feature automatic="unselected" manual="undefined" name="AddRoleAtSetup"/><feature automatic="unselected" manual="undefined" name="AddRoleDynamically"/><feature automatic="selected" manual="undefined" name="Storage"/><feature automatic="selected" manual="undefined" name="StorageType"/><feature automatic="unselected" manual="undefined" name="RecordHistory"/><feature automatic="unselected" manual="undefined" name="StructuredRecords"/><feature automatic="unselected" manual="undefined" name="HashRecords"/><feature automatic="selected" manual="undefined" name="ContractsMetadata"/><feature automatic="selected" manual="undefined" name="AssetsData"/><feature automatic="selected" manual="undefined" name="StateData"/><feature automatic="selected" manual="undefined" name="StorageEmplacement"/><feature automatic="selected" manual="undefined" name="OffChain"/><feature automatic="selected" manual="undefined" name="Database"/><feature automatic="selected" manual="undefined" name="OnChain"/><feature automatic="undefined" manual="selected" name="EventsEmission"/><feature automatic="selected" manual="undefined" name="DataContracts"/><feature automatic="selected" manual="undefined" name="Frontend"/><feature automatic="selected" manual="undefined" name="DeploymentView"/><feature automatic="selected" manual="undefined" name="TraceabilitySetup"/><feature automatic="unselected" manual="undefined" name="RecordsCollectionsSetup"/><feature automatic="selected" manual="undefined" name="AssetSetup"/><feature automatic="selected" manual="undefined" name="StateMachineSetup"/><feature automatic="selected" manual="undefined" name="BlockchainNetwork"/><feature automatic="undefined" manual="selected" name="EthereumMainnet"/><feature automatic="unselected" manual="undefined" name="EthereumTestnet"/><feature automatic="selected" manual="undefined" name="ParticipantsSetup"/><feature automatic="selected" manual="undefined" name="IndividualsSetup"/><feature automatic="undefined" manual="unselected" name="RolesSetup"/><feature automatic="selected" manual="undefined" name="ManagementView"/></configuration>
101 changes: 101 additions & 0 deletions evaluation/P1/contracts/Factory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >0.8.0;

import "./controller/ParticipantsController.sol";
import "./data/Participants.sol";


import "./data/StateMachine.sol";
import "./controller/StateMachineController.sol";

import "./data/Assets.sol";
import "./controller/AssetsController.sol";

contract Factory {
struct Contracts {
ParticipantsController participantsControllerContract;
Participants participantsContract;
StateMachine stateMachineContract;
StateMachineController stateMachineControllerContract;
Assets assetsContract;
AssetsController assetsControllerContract;
}

// Reduces the number of template-used comments
struct FactoryParameters {
address[] owners;
Participants.Participant[] participants;
StateMachine.State[][] statesCollections;
string[] collectionNames;
Assets.Asset[] assets;
}

address[] owners;
Contracts contracts;

constructor (FactoryParameters memory factoryParameters) {
owners = factoryParameters.owners;

participantsFactory(factoryParameters);


stateMachineFactory(factoryParameters);

assetsFactory(factoryParameters);
}

function participantsFactory(FactoryParameters memory factoryParameters) private {
contracts.participantsContract = new Participants(
address(this)
,factoryParameters.participants
);

contracts.participantsControllerContract = new ParticipantsController(
address(this),
address(contracts.participantsContract)
);

contracts.participantsContract.assignController(address(contracts.participantsControllerContract));
}


function stateMachineFactory(FactoryParameters memory factoryParameters) private {
contracts.stateMachineContract = new StateMachine(
address(this),
address(contracts.participantsContract),
factoryParameters.statesCollections,
factoryParameters.collectionNames
);

contracts.stateMachineControllerContract = new StateMachineController(
address(this),
address(contracts.stateMachineContract),
address(contracts.participantsContract)
);

contracts.stateMachineContract.assignController(address(contracts.stateMachineControllerContract));
}

function assetsFactory(FactoryParameters memory factoryParameters) private {
contracts.assetsContract = new Assets(
address(this),
factoryParameters.assets
);

contracts.assetsControllerContract = new AssetsController(
address(this),
address(contracts.participantsContract),
address(contracts.assetsContract)
);

contracts.assetsContract.assignController(address(contracts.assetsControllerContract));
}

function getContractsAddresses() public view returns (Contracts memory) {
return contracts;
}

// TODO: create a Proxy and implement features as below
// TODO: implement upgrade of contracts
// TODO: implement function relay to controllers
}
85 changes: 85 additions & 0 deletions evaluation/P1/contracts/controller/AssetsController.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >0.8.0;

import "../data/Assets.sol";
import "../data/Participants.sol";

contract AssetsController {
// ---- STATES ---- //
address factory;
Assets assetsContract;
Participants participantsContract;

/* #EventsEmission */
// ---- EVENTS ---- //
event NewOwner(string indexed assetName, address indexed newOwner);
event NewAsset(Assets.Asset indexed asset);
event NewAttachedData(Assets.Asset indexed asset, string indexed key, string indexed value);
event NewDetachedData(Assets.Asset indexed asset, string indexed key);
/* /EventsEmission */

constructor(
address _factory,
address _participantsAddr,
address _assetsContractAddr
)
{
factory = _factory;
participantsContract = Participants(_participantsAddr);
assetsContract = Assets(_assetsContractAddr);
}

function getFactory() public view returns (address) {
return factory;
}

function getAssetsContractAddress() public view returns (address) {
return address(assetsContract);
}

function changeOwner(string memory _assetName, address _newOwner) public {
address currentOwner = assetsContract.getAsset(_assetName).owner;
require(currentOwner == msg.sender, "Sender is not the owner of the asset.");

assetsContract.changeOwner(_assetName, _newOwner);

/* #EventsEmission */
emit NewOwner(_assetName, _newOwner);
/* /EventsEmission */
}

function newAsset(string memory _assetName, bytes32 _assetDesc, string[2][] memory _attachedData) public {
require(participantsContract.doesParticipantExist(msg.sender), "Participant does not exist.");

assetsContract.newAsset(_assetName, _assetDesc, msg.sender, _attachedData);
/* #EventsEmission */
emit NewAsset(assetsContract.getAsset(_assetName));
/* /EventsEmission */
}

function attachData(string memory _assetName, string memory _key, string memory _value) public {
address currentOwner = assetsContract.getAsset(_assetName).owner;
require(currentOwner == msg.sender, "Sender is not the owner of the asset.");

assetsContract.attachData(_assetName, _key, _value);

/* #EventsEmission */
emit NewAttachedData(assetsContract.getAsset(_assetName), _key, _value);
/* /EventsEmission */
}

function detachData(string memory _assetName, string memory _key) public {
address currentOwner = assetsContract.getAsset(_assetName).owner;
require(currentOwner == msg.sender, "Sender is not the owner of the asset.");

assetsContract.detachData(_assetName, _key);

/* #EventsEmission */
emit NewDetachedData(assetsContract.getAsset(_assetName), _key);
/* /EventsEmission */
}

function getAttachedData(string memory _assetName, string memory _key) public view returns (string memory) {
return assetsContract.getAttachedData(_assetName, _key);
}
}
Loading

0 comments on commit f5b42a7

Please sign in to comment.