Open Data Trusted Anchors - Project
The description here under gives all the detail on how the SC is destined, how to install it, how to test it.
Introduction
The Open Data Trusted Anchors (ODTA) project is described under Git https://github.com/IPConvergence/TrustedDistributedOpenDataRegistry
Different illustration video has been done to better understand the Project Code Repo:
Javascript Test on solidity code + explanation of the Code Repo structure: https://youtu.be/mPddwMQAbMo
Demonstrator of the end to end Web Demo Application: https://youtu.be/iRPzkw89-WQ
Integration of the ODTA Registry with Multi-sig Wallet: https://youtu.be/o_f6MhY5Gio
Deployment of the ODTA Registry on Rinkeby Testnet: https://youtu.be/-y7qY440Pv4
Data Asset JSON Modeling
Example of a JSON/JSON-LD Data Asset of a CO2 Measure done by a City Sensor
The bruto "dataAssetValue" is:
{ "SensorID":"232939923"
"SensorLocation": "Brussels"
"created": "2020-10-30T19:19:49Z"
"Co2Value": "100"
"Unit": "g/km"
}
The Data Producer must enrich that "dataAssetValue" with MetaData and produce a DataAsset Envelop formated in JSON, linked Data in order to be exchanged between Data Producer and Data Consumer as illustrated here under. Each use case will be in charge of formating the "dataAssetValue" according to the model here under.
Some of the MetaData will be anchored on the Open Data Trusted Anchor Smart Contract to enable trusted data exchange as explained in the project Github page.
{
"@context": [
"https://schema.org/"
],
"dataAssetDomain": "Climat",
"dataAssetProducerName": "CityOfBrussels",
"dataAssetProducerID": "PublicKeySensor",
"dataAssetID": "HashOfBase64(FullJsonWithoutDataAssetID attribute)",
"dataAssetValue":
{"SensorID":"232939923"
"SensorLocation": "Brussels"
"created": "2020-10-30T19:19:49Z"
"Co2Value": "100"
"Unit": "g/km"
},
"dataAssetAccessType": "paying",
"dataAssetAccessCondition": "https://....",
"dataAssetUse&ProcessingConditions": "https://...",
"proofOfIntegrityDataAsset": "LedgerTransactionProof",
"proofOfSourceAuthenticity": "LedgerTransactionProof",
"proofOfIntegrityUseProcessingConditions": "LedgerTransactionProof",
"proof": {
"type": "EcdsaSecp256k1Signature2019",
"created": "2020-10-30T19:20:50Z",
"proofPurpose": "assertionMethod",
"verificationMethod": "PublicKeyDataProducer",
"jws": "xxxxxxxxxx"
}
}
PropertyName
Property Type
Requ. JSON
Requ. On SC
Description
Context
String
Optional
No
Semantic Web Context URL if published on a Registry
Data Asset Domain
String
Optional
Yes
Name of the domain to which is linked the Data Asset
Data Asset Producer Name
String
Optional
Yes
Name of the Data Asset Producer
Data Asset Producer ID
String
Mandatory
Yes
Unique Data Producer ID, can be a public key
Data Asset ID
String
Mandatory
Yes
Unique identifier of the Data Asset, will be the hash of the base64 encoded version of the full JSON
Data Asset Value
JSON/ String
Mandatory
No
JSON including the Data Asset Content value
Data Asset Access Type
String
Mandatory
Yes
Type of Access linked to the Data Asset: Free or payable
Data Asset Access Condition
String
Optional
No
URL pointing to the describing ot the Data Asset Access Conditions, like price and modality if payable
Data Asset Use & Processing Conditions
String
Optional
No
URL pointing to the describing ot the Data Asset Access Use & Processing Conditions
Proof Of Integrity Data Asset
String
Mandatory
Yes
Hash of the JSON of the Data Asset
Proof Of Source Authenticity
String
Mandatory
Yes
Hash of PublicKey of Data Producer
Proof of Integrity Use & Processing Conditions
String
Mandatory
Yes
Hash of the Data Asset Access Use & Processing Conditions document
proof
JSON/String
Optional
No
JSON-LD Signature of the Data Producer of the Asset
Smart Contract Data Modeling
We will define here under the resources that will be created at SmartContract Level to manage the anchoring of the required Data Asset information needed to deliver trust services.
The following resources will be needed:
Data Producer
Data Consumer
Data Asset
In the rest of the document when playing about a KeyValue Map data structure, we will use "KVM" acronym.
Objects: used by Data Administrator of SmartContract
Object
Type
Description
stopped
bool
Variable used to put on pause a SmartContract by the SC administrator
owner
address
Address of the SmartContract owner
operatorSC
address
Address of the Operator of the SC, that can put it in pause mode also
Objects: used by Data Producer
Object
Type
Description
dataProducerStore
mapping (address => bytes32[])
KVM(dataProducerID -> dataProducerDataAssetList)
dataProducerDataAssetList
bytes32 []
Array of dataAssetID
Objects: for managing Data Consumer
Object
Type
Description
dataAssetConsumerID
address
Unique ID identifying a data consumer. For this project, we will use the Ethereum Address of the user
Objects: for managing Data Asset
Object
Type
Description
dataAssetIDList
mapping (bytes32 => bool)
KVM(dataAssetID -> bool)
dataAssetStore
mapping (bytes32 => dataAssetObject)
KVM (dataAssetID -> dataAssetObject)
dataAssetObject
struct
{
address dataAssetProducerID;
accessType dataAssetAccessType;
uint256 dataAssetAccessPrice;
string dataAssetAccessDuration;
bytes32 proofOfIntegrigyDataAsset;
bytes32 proofOfSourceAuthenticity;
bytes32 proofOfIntegrityUseProcessingConditions;
}
dataAssetAccessStore
mapping (bytes32 => mapping(address => dataAssetAccessObject))
KVM (dataAssetID -> KVM(dataAssetConsumerID -> dataAssetAccessObject))
dataAssetAccessObject
struct
{
bool dataAssetAccessStatus;
string dataAssetAccessStartDate;
string dataAssetAccessEndDate;
}
accessType
enum
{free, paying}
dataAssetTotal
uint256
Count the number of Data Asset Anchored on the Registry
SmartContract Design
In this section, we will define the SC Architecture used. The Registry will be implemented with 3 Smart Contracts:
ODTA Validator Smart Contract -> Main SC logic
ODTA Storage Smart Contract -> Diamond Storage Illustration
ODTA Proxy Smart Contract -> recommand to use Open Zepplin SC (out of scope of this first version of the project)
Gnosis Multi-Sig Smart Contract -> reuse Gnosis Multi-Sig Wallet for operator of SC
Using a Library in ODTAValidator SC
On the ODTAValidator SC, there is a state variable that is counting the number of DataAsset enchored on the ODTA. In order to avoid any overflow, we use a MathSage.sol lib managing it.
// On the ODTAValidator.sol
// Import a lib managing overflow on add/sub
import "./utils/SafeMath.sol";
contract ODTAValidator {
using SafeMath for uint256; // To use the lib in the SC
....
function insertDataAsset (.........{
....
// Avoid Owerflow on addition of value
dataAssetTotal=dataAssetTotal.add(1);
}
Design Pattern
Circuit breaker / Emergency stop pattern
// Circuit Breakers (Pause on all function) accessible for SC Owner
// Following has been added to the ODTAValidator.sol
// -- Objects used by SmartContract Administrator --
bool private stopped;
// isOwner modifer: checks if the msg.sender is the owner of the contract
modifier isOwner() {
require(msg.sender == owner);
_;
}
/// stopInEmergency: when there is a bug and the SC is put in pause by Owner, the stopped = true, meaning that the condition here under is not valid
/// and the function having that modifier can not execute anymore
modifier stopInEmergency() { if (!stopped) _; }
/// isProducerOfExistingDataAsset: check if the msg.send is well the DataProducer of an existing DataAsset well present in dataProducerStore
constructor() public{
/* set the owner as the person who instantiated the contract. */
owner = msg.sender;
dataAssetTotal = 0;
stopped = false; // Used by Circuit Breakers, pause functionality
}
/** @dev Function in charge of inserting a Data Asset on SC
*/
function toggleContractActive() isOwner() public {
stopped = !stopped;
}
// Then add the modifier on all the method that can be put in break
function setDataAssetAccess(bytes32 _dataAssetID, address _dataAssetConsumerID,
bool _access) public isProducerOfExistingDataAsset(_dataAssetID)
stopInEmergency(){ ...}
Assembly code demonstrating inside SC
We have define as illustration a Diamond Storage on ODTAStorage.sol
// Assembly code used to define Diamond Storage to store the SC version:
// ODTAStorage.sol
contract ODTAStorage {
// State variable to have a unique position on memory to store the struct
bytes32 public constant ODTAStorage_DIAMOND_STORAGE_POSITION = keccak256(
"diamond.standard.ODTA.storage"
);
struct ODTA {
uint256 _version;
}
/** @dev Funtion that creates a Diamond Storage to store the ODTA struct and returns the storage pointer to the ODTA Struct
*/
function odtaStorage() internal pure returns (ODTA storage ms) {
bytes32 position = ODTAStorage_DIAMOND_STORAGE_POSITION;
assembly {
ms.slot := position
}
}
}
Current ODTA SC - Administration and Upgradability
As the current project focus on setting up an ODTA application specific SC, we have used the design pattern of a Monolithic SC, also to focus on an immutable character of that SC, that could not be upgraded later on to avoid risk of someone manipulating the logic and content of the SC.
If in your application you need upgradability pattern support, meaning keeping a unique SC address for all the file and supporting upgradability of the logic of the SC and also keeping all the existing data anchored inside of it, we recommend then the OpenZepplin Proxy Design Pattern, combined with a Diamond Storage instanciated on the proxy.
Proxy Design Pattern - For Upgradability - Guidelines to support it
We have focus on this project on the creation of the ODTA application specific SmartContract. If you want to support upgradability of the SC without changing the address of the SC, we recommand you to use the OpenZepplin Proxy component implementation. Please refer to the https://github.com/OpenZeppelin/openzeppelin-sdk/tree/master/packages/lib/contracts/upgradeability
The idea is that you communicate to the public consumers the address of the OpenZepplin deployed Proxy SC for your ODTA. Then on The OpenZepplin Proxy ODTA SC, you point to the address of the ODTA SC referenced in this project. In order to support full upgradability, meaning that anytime you upgrade the ODTA SC implementation of a specific version, you don't loose the data, you need to define on the Proxy SC all the Diamond Storage for all the data objects used on the ODTA implementation. To give you an example, we have implemented on the ODTA project here one ODTAStorage.sol SC defining a DiamondStorage. We defined in the ODTAStorave.sol a "struct" that contain the version number of the SC. You should need to place in that struct all the other objects used on the ODTAValidator to support this full upgradability.
Using Multi-Sig Wallet for Operator on the ODTA
We recommand to use the Gnosis MultiSig SC to define a MultiSig Wallet for the operator of the ODTA SC. Please use the tool https://wallet.gnosis.pm/, simply connect your Meta Mask to your Web3ServiceProvider (so to your target Ledger), define a multisig SC and deploy it.

// test javascript on ./project/test
let ODTAValidator = artifacts.require('ODTAValidator')
contract('ODTAValidator', function(accounts) {
const owner = accounts[0]
const dataAssetProducerID = accounts[1]
const dataAssetConsumerID1 = accounts[2]
const dataAssetConsumerID2 = accounts[3]
let instance
beforeEach(async () => {
instance = await ODTAValidator.new()
})
it("Step 10: Owner set operator (can be the multisig SC address), trying change SC version with operator", async() => {
let instance2 = await ODTAValidator.at("0x45c6028Ae0d9cF045f52c96c1a84727c6c114Cc6") // insert here the address of your deployed SC
const result1 = await instance2.getOperator({from: owner})
const result2 = await instance2.setOperator("0x71C78822B2C7fBc5FaB801a167dB930399C9d586",{from: owner}) // Specify here your multi sig wallet address
const result3 = await instance2.getOperator({from: owner})
console.log(`
"Output Step 10: Owner set operator (can be the multisig SC address), trying change SC version with operato
"Address of the Previous Operator:" ${result1}
"Address of the new Operator - (can be MultiSig SC):" ${result3}
`)
})
})
Then still from gnosis UI URL link here above perform transaction to the ODTA SC Address by selecting the ABI method setSCVersion() from the multisig, after having setup via the method setOperator from the owner adress the mutlisig wallet address.
// ABI of the setSCVersion() Method
[
{
"inputs": [
{
"internalType": "uint256",
"name": "version",
"type": "uint256"
}
],
"name": "setSCVersion",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]




// You can test with a javascript testscript under ./project/test/testing.js
let ODTAValidator = artifacts.require('ODTAValidator')
contract('ODTAValidator', function(accounts) {
const owner = accounts[0]
const dataAssetProducerID = accounts[1]
const dataAssetConsumerID1 = accounts[2]
const dataAssetConsumerID2 = accounts[3]
let instance
beforeEach(async () => {
instance = await ODTAValidator.new()
})
it("Step 10: Verify the SC Version has been changed by multisig wallet", async() => {
let instance2 = await ODTAValidator.at("0x45c6028Ae0d9cF045f52c96c1a84727c6c114Cc6") // insert here the address of your deployed SC
const result0 = await instance2.getSCVersion({from: owner})
const result1 = await instance2.getOperator({from: owner})
console.log(`
"Output Step 10: Verify the SC Version has been changed by multisig wallet"
"Current Version of the SC:" ${result0}
"Address of the Previous Operator:" ${result1}
`)
})
})
See video as illustration on ODTA integration with Gnosis MultiSig Wallet https://youtu.be/o_f6MhY5Gio, the demo has been done with my local ganache-cli
Just as info, if you want to understand the UI code:https://github.com/gnosis/MultiSigWallet/tree/master/dapp,
Testing Code Security/Quality
The Code of ODTAValidator.sol has been tested with MythX (https://mythx.io)
First install the MythX security plugin for Truffle Framework
$ npm install -g truffle-security
Then run the Test of your smartcontract code
$ truffle run verify --apiKey "APIKeyComingFromYourMythXDashboard"
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIxYzU4MzE0Mi1lZDhkLTRjODEtOGUyYS0yYjE4MDk0MzZiN2QiLCJpYXQiOjE2MDU0NDUyMDQuMzE4LCJpc3MiOiJNeXRoWCBBUEkiLCJleHAiOjE5MjEwMjEyMDQuMzAzLCJ1c2VySWQiOiI1Zjk1NmE2ZWE4ZjAwNTAwMTg4ODUzYjkifQ.bY17BpoHjg9npqkfIeeZI0v0zJVutTsMrFmf9VeJozg
You can validate the result in your dashboard on Mythx
There is a Javascript code that has been done to test the functionalities of the SC. This is under "/Project/Test/Test_ODTAValidator.js
// To launch the Test on the ODTAVAlidator.sol SC
// In CLI, go in the project directory and run the following command
$: truffle test
The video demonstrating the good result of the test is on: https://youtu.be/mPddwMQAbMo
Integration with IPFS (In Option)
As as complementary to the present project project, please read on https://github.com/IPConvergence/ipfs-eth-anchoring
There is a sample demonstrator that enables to anchor on IPFS any input documents. This can be used in complementary to this ODTA application to store any Data Asset JSON File on IPFS and have the hash value of it. Then this hash value can be anchored on the ODTA Registry which is the focus of the present project.
!!! Don't consider the SC part of this IPFS demonstrator, this is not part of this project.
// you can import on IPS Data Asset in JSON Format: AssetTemp1.json
{
"name": "Co2 Measure 1",
"dataAssetID": "0x9ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
"dataAssetValue":{
"sensorID":"245939811",
"sensorLocation": "BXL",
"created": "2020-11-10T19:19:49Z",
"co2Value": "75",
"unit": "g/km"
},
"dataAssetProducerID": "0x7C584e710fE6afA5369A8Bffee47Fc98D76535a5",
"dataAssetAccessType": "paying",
"dataAssetAccessPrice": "100000000000000000"
}

SmartContract Methods
In this section, we will define for each SC, the signature of each method defined in it.
Function
Input
Output
insertDataAsset
(address _dataAssetProducerID,
string _dataAssetAccessType,
uint256 _dataAssetAccessPrice,
string _dataAssetAccessDuration,
bytes32 _proofOfIntegrigyDataAsset,
bytes32 _proofOfSourceAuthenticity,
bytes32 _proofOfIntegrityUse&ProcessingConditions
)
getDataAsset
(bytes32 _dataAssetID)
(address _dataAssetProducerID,
accessType _dataAssetAccessType,
string _dataAssetAccessPrice,
string _dataAssetAccessDuration,
bytes32 _proofOfIntegrigyDataAsset,
bytes32 _proofOfSourceAuthenticity,
bytes32 _proofOfIntegrityUse&ProcessingCondictions
)
+ generate eventGetDataAccess (address _dataAssetProducerID, address _dataAssetConsumerID, bytes32 _dataAssetID)
getDataAssetAccessType
(bytes32 _dataAssetID)
(accessType _dataAssetAccessType)
insertDataAssetAccess
(bytes32 _dataAssetID, address _dataAssetConsumerID)
getDataAssetAccess
(bytes32 _dataAssetID, address _dataAssetConsumerID)
(bool _access)
getDataAssetTotal
()
(uint256 _dataAssetTotal)
payToAccessDataAsset
(bytes32 _dataAssetID, address payable _toDestination)
Not yet implemented in version v1.0
toggleContractActive
()
Circuit Breaker-Pause that can only be called by the owner or operator of the SC. It put all the method on pause if invoked.
setOperator
(address _operatorSC)
Function called by SC owner to setup an operator of it (to call Circuit Breaker-Pause)
getOperator
()
(address _operatorSC), return address of operator of the SC
setSCVersion
(uint256 version)
Set the SC version number on a Diamond storage
getSCVersion
()
(uint256) return the SC version number on Diamond storage
Front-End Demo Web Server
Here under we explain the Front-End Demo Web Server we implement to show a demonstrator Consumer Wep Application to interact with the ODTA SmartContract.
Demo Video
The Demo of the Frond-End Application is on : https://youtu.be/iRPzkw89-WQ
We will use the npm lite Server. Setup instruction are on https://www.npmjs.com/package/lite-server
// Install NPM lite Server package on the directory of the project
$ cd /ProjectDirectory
// Install the NPM lite-server
$ npm install lite-server
// Create in the project root directory a file package.json
// This is the config file used by NPM and add the following inside of it:
# Inside package.json...
"scripts": {
"dev": "lite-server"
}
// Then you can launch the lite-Server npm package
$ npm run dev
// if you run it on a server, launch it as a dedicated process:
$: nohup npm run dev >> runningwebserver.log 2>&1 &
// in runningwebserver.log -> you will have all the npm lite web server output
// You have a Local Web Server running on http://localhost:3000
// Copy on the root section of your project an index.html file
//Add "Hello World" inside of it
/Then refresh the browser page you should see your first web page
// To Stop the running lite-server, do in the CLI running windows and enter
// ctrl + C
// To specify a src base directory for your project, create a bs-config.json
// in your project's folder with the following content:
{
"port": 8000,
"files": ["./src/**/*.{html,htm,css,js}"],
"server": { "baseDir": "./src" }
}
// Then in the project directory create a folder:
$ mkdir src
$ cd src
// create an index.html file that will be the host page of the Demon
// Open the Chrome Browser and go to view/developer/Javascrip console for
//debugging
We have setup for demo the Front End Demonstrator on a server, that you can use to test it:
Deploy ODTA SC
Deploy ODTA SC on Local Ganache Network
// Add the following line on truffle-config.js
networks: {
development: {
host: "127.0.0.1", // Localhost (default: none)
port: 8545, // Standard Ganache port (default: none)
network_id: "*", // Any network (default: none)
}
}
// Compile the ODTA SC
$: truffle compile
// Deploy ODTA SC on local ganache
$: truffle migrate -- reset
// validate the transaction with the Ganache
Deploy ODTA SC on Rinkeby Public Ethereum Test Network
// Install the Wallet Component from npm that you will need
$: npm install @truffle/hdwallet-provider
// Update the truffle-config.js
const HDWallet = require('@truffle/hdwallet-provider');
//Copy here under your infura API KEY
const infuraURL = 'https://rinkeby.infura.io/v3/YourInfuraKeyAPI';
const fs = require('fs');
// Put in your project repo a file ".secret" with the mnemonic of you Rinkey user
// Never to this for real money account
const mnemonic = fs.readFileSync(".secret").toString().trim();
// Add in the network section the parameters to connect ot Rinkebey
networks: {
rinkeby: {
provider: () => new HDWallet(mnemonic, infuraURL),
network_id: 4, // Rinkeby's network id
gas: 5500000,
}
}
// Compile and deploy on Rinkeby Network the project SmartContract
$: truffle migrate --network rinkeby --reset
// use only --reset if you have already deploy it.
Deployment of the ODTA Registry on Rinkeby Testnet: https://youtu.be/-y7qY440Pv4
Last updated
Was this helpful?