Data Structures
A detailed explanation of all aspects of the Solidity language can be found on the official Solidity language guide online (https://soliditylang.org/ (opens in a new tab)). We will only cover the aspects required for our solution.
Smart Contracts are used on Blockchain networks to provide the ability for external components to send data to be saved on the Blockchain or used by the Blockchain for processing. Smart Contracts use state variables whose values are permanently stored in contract storage.
In this implementation, the Smart Contract will store order data with expected values for various order parameters and then receive and store actual values for the parameters from sensors. The actual and expected values will be compared and penalties will be calculated based on defined rules and values specified in the order.
Data structures in Solidity are broadly similar to other programming languages with some variations and limitations. It supports the standard primitives and arrays with some limitations. Multiple primitives can be grouped together into a variable of type struct (which is similar to an object in an Object Oriented language). A struct can include other structs.
Solidity uses a concept referred to as mapping. This is equivalent to a hash table or a dictionary structure with key-value pairs in other languages. The key for a mapping should ideally be the primary identifier for an entity (such as the account address of a participant). This is recommeded since a key should be a value which you will receive as an input and use that to retrieve the associated entity from the mapping.
A few key points to remember:
-
In Solidity, there is no null value. If you return the value of a variable that has not been assigned any value, a default value will be returned depending on the datatype of the variable (false for boolean, 0 for int, empty tuple for a tuple, and so on). Since these are values that are likely to be valid values for the solution, an unused value will have to be agreed upon to be treated as a null value.
-
Solidity does not support decimal values so all values must be integers, including sensor data.
-
All ether amounts in the contract are stored and processed in wei by default.
Read up on the various ether denominations to understand how this impacts your design. This is also explained in the web app user guide.
We will require the following data structures in the Smart Contract for our solution:
Participant struct
struct Participant {
uint8 id;
string name;
string role;
address account_address;
bool contract_access;
}
Participant - Account Address mapping
mapping(address => Participant) public Participants;
Participant Private Key - Account Address Mapping
Note that this is not advisable in a real solution as it is a huge security risk. This has been included only to simplify the learning solution.
mapping(address => string) PrivateKeys;
Role - Participant Account Address mapping
A redundant mapping to validate that two participants are not mapped to the same role.
mapping(string => address) public RoleParticipant;
Order Item struct
struct item_struct {
string description;
int order_quantity;
string uom;
int penalty_amount; // amount is in wei
}
Vehicle struct
struct vehicle_struct {
string assigned_vehicle_regn_num;
int penalty_amount; // amount is in wei
}
Schedule struct
struct schedule_struct {
int expected_departure_timestamp;
int expected_arrival_timestamp;
int penalty_amount; // amount is in wei
}
Transport Conditions struct
struct transport_condition_parameter_struct {
string name; //temperature, humidity, etc
int min_value;
int max_value;
int penalty_amount; // amount is in wei
}
Order struct
This includes some of the structs described above.
struct order_struct {
uint8 order_id;
address[] participant_addresses;
int buyer_to_seller_amount;
int seller_to_transporter_amount;
item_struct item;
schedule_struct schedule;
transport_condition_parameter_struct[] transport_condition_parameters;
vehicle_struct vehicle;
string status;
}
Order Id - Order mapping
mapping(uint8 => order_struct) orders;
Parameter struct
struct sensor_parameter_struct {
string name;
int value; // This is assuming all sensor data will be integers.
}
Sensor Data struct
struct sensor_data_struct {
uint8 sensor_id;
sensor_parameter_struct[] sensor_parameters;
}
Microcontroller Data struct
This includes an array of sensor data structs which in turm includes an array of parameter structs thus allowing for a microcontroller to have multiple sensors and for each sensor to have multiple parameters.
struct microcontroller_data_struct {
//Data coming from the microcontroller
uint8 microcontroller_id;
uint timestamp;
sensor_data_struct[] sensor_data;
}
Incoming (Sensor) Data struct
struct incoming_data_struct {
string loading_vehicle_regn_num;
string unloading_vehicle_regn_num;
int departure_timestamp;
int arrival_timestamp;
int loaded_quantity; // This is to calculate any difference in unloaded quantity.
int unloaded_quantity;
microcontroller_data_struct[] microcontroller_data;
}
Order Id - Incoming Data mapping
mapping(uint8 => incoming_data_struct) incoming_data;
Penalties struct
struct penalty_struct {
address participant_address;
int penalty_amount; // amount is in wei
string penalty_parameter_name; // temperature, humidity, etc
}
Order Id - Penalty struct array mapping
mapping(uint8 => penalty_struct[]) penalties;