Using blockchain to store medical data
The question of whether blockchain is suitable for public data is complex and there is no one-size-fits-all solution. However, there are some potential solutions to consider:
- Private Networks: One solution to the privacy concerns surrounding blockchain technology is to use private blockchains instead of public blockchains. Private blockchains can offer the benefits of blockchain technology, such as immutability and transparency, while also ensuring data privacy and security.
- Hybrid Approach: Another option is to use a hybrid approach where sensitive data is stored on a private blockchain and non-sensitive data is stored on a public blockchain. This enables you to enjoy the benefits of both blockchains while keeping sensitive information safe.
- Layered architecture: A layered architecture can be used to separate sensitive data from non-sensitive data, ensuring that sensitive information is stored securely while non-sensitive data can be stored on a public blockchain.
- Encryption: Encryption can be used to keep sensitive data safe. This can provide an extra layer of security and help prevent unauthorized access to sensitive information.
Smart contract development
Define variables to store the contract owner, medical records, authorized users, and access restrictions for each record. records
and accessRestrictions
variables are maps, key-value stores, where keys and values can be of any type.
records
A map maps keys of type bytes32
to Record
values of type , the latter being a structure defined later in the contract. authorizedUsers
The mapping maps addresses to boolean values indicating whether a user can access a record. accessRestrictions
A map maps bytes32
keys to Restrictions
values of type , which is also the structure defined later by the contract.
pragma solidity ^0.7.0;0.7.0;
// 在使用任何智能合约之前彻底审查和测试它是很重要的。
contract MedicalRecord {
// 合同所有者的地址。
address private owner;
// 正在存储的医疗记录。
mapping(bytes32 => Record) private records;
// 允许访问记录的授权用户。
mapping(address => bool) private authorizedUsers;
// 对每条记录的访问限制。
mapping(bytes32 => Restrictions) private accessRestrictions;
The lines given below define the "Record" and "Restrictions" structures, which are used to store the data of a single medical record, and the access restrictions on a single record, respectively. Record
The structure has two fields: data
, which is a bytes
value that stores the actual medical record data, and timestamp
, which is a uint256
value that stores the last time the record was added or updated. Restrictions
The structure contains three fields: authorizedUsers
, which is a map that stores the users who have access to the record, , minRole
which is a uint8
value that stores the minimum roles required to access the record, and expirationDate, which is a uint256 value that stores The record's expiration date.
// 用户可以拥有的角色。
// The data for a single medical record.
struct Record {
bytes data;
uint256 timestamp;
}
// 对访问单个记录的限制。
struct Restrictions {
// 允许访问记录的用户。
mapping(address => bool) authorizedUsers;
// 访问记录所需的最低角色。
uint8 minRole;
// The expiration date for the record.
uint256 expirationDate;
}
Now, define an (enumeration) type Roles
named enum
to represent the different roles a user can have. enum
Has three possible values: Patient
, , Doctor
and Administrator
.
// 用户可以拥有的角色。
enum Roles {
Patient,
Doctor,
Administrator
}
Create a mapping name "userRoles" that maps addresses to values of type "Roles", which is the "enum" defined earlier. This map is used to store each user's role. Here's what it looks like:
// 每个用户的角色。
mapping(address => Roles) private userRoles;
Create a contract constructor, called when deploying a contract. The constructor owner
sets the variable to the address of the user who deployed the contract. Here's how to do it:
// 构造函数设置合约的所有者。
constructor() public {
owner = msg.sender;
}
Now, create functions to allow the contract owner to authorize or revoke user access to records and set the user's role. authorizedUsers
The map is updated to reflect the change in authorization status. userRoles
Mappings have also been updated to reflect the role changes. This is what it looks like:
// 只有所有者才能授权用户访问记录。
function authorizeUser(address user) public onlyOwner {
authorizedUsers[user] = true;
}
// 只有所有者才能取消用户访问记录的授权。
function removeAuthorization(address user) public onlyOwner {
authorizedUsers[user] = false;
}
// 只有所有者才能设置用户的角色。
function setUserRole(address user, Roles role) public onlyOwner {
userRoles[user] = role;
}
Now, create a function that allows authorized users to retrieve medical records. The function takes as input a key of type "bytes32" that identifies the record. The function first retrieves the record and its restrictions from storage using the "records" and "accessRestrictions" maps. The function then checks that the current time is before the record's expiration date, that the user's role is equal to or higher than the minimum role required to access the record, and that the user has permission to access the record.
If any of these conditions are not met, the function will throw an error. If all conditions are met, the function will return the recorded data. You may see the following:
// 如果当前时间在记录的到期日期之前,则只有授权用户才能访问记录
// 并且用户的角色等于或高于访问记录所需的最低角色。
function getRecord(bytes32 key) public view onlyAuthorized returns (bytes memory) {
Record storage record = records[key];
Restrictions storage restrictions = accessRestrictions[key];
require(block.timestamp < restrictions.expirationDate, "This record has expired.");
require(uint8(userRoles[msg.sender]) >= restrictions.minRole, "Your role is not high enough to access this record.");
require(restrictions.authorizedUsers[msg.sender], "You are not authorized to access this record.");
return record.data;
}
setRecord
Functions allow owners to add or update medical records. The function takes a key and record data of type "bytes32" as input and an expiration date. It looks like this:
// 只有所有者可以添加或更新记录。
function setRecord(bytes32 key, bytes memory data, uint256 expirationDate, uint8 minRole, address[] memory authorizedUsers) public onlyOwner {
Record storage record = records[key];
record.data = data;
record.timestamp = block.timestamp;
Restrictions storage restrictions = accessRestrictions[key];
restrictions.expirationDate = expirationDate;
restrictions.minRole = minRole;
for (uint i = 0; i < authorizedUsers.length; i++) {
restrictions.authorizedUsers[authorizedUsers[i]] = true;
}
}
deleteRecord
Feature allows owner to delete medical records. The function takes as input a key of type "bytes32" and uses the "delete" keyword to delete the record and its limits from storage. You can do this:
// 只有所有者才能删除记录。
function deleteRecord(bytes32 key) public onlyOwner {
delete records[key];
delete accessRestrictions[key];
}
The following line defines two "decorator" functions: "onlyOwner" and "onlyAuthorized". modifier
is a function that can be used to modify the behavior of other functions. onlyOwner
Modifiers can be applied to functions to restrict the function to be called only by the contract owner.
onlyAuthorized
Modifiers can be applied to functions to restrict that function to be called only by authorized users. modifier
Functions use require
the keyword to enforce limits. require
The function throws an error if the conditions specified in the statement are not met .
// 只有所有者才能调用这些函数。
modifier onlyOwner {
require(msg.sender == owner, "Only the owner can perform this action.");
_;
}
// 只有授权用户才能调用这些函数。
modifier onlyAuthorized {
require(authorizedUsers[msg.sender], "You are not authorized to perform this action.");
_;
}
}
full code
The contract allows owners to store medical records, set restrictions on access to each record, and authorize users to access records. These restrictions include expiration dates, minimum required roles, and authorized user lists. The contract also has a map to store each user's role.
pragma solidity ^0.7.0;
contract MedicalRecord {
address private owner;
mapping(bytes32 => Record) private records;
mapping(address => bool) private authorizedUsers;
mapping(bytes32 => Restrictions) private accessRestrictions;
struct Record {
bytes data;
uint256 timestamp;
}
struct Restrictions {
mapping(address => bool) authorizedUsers;
uint8 minRole;
uint256 expirationDate;
}
enum Roles {
Patient,
Doctor,
Administrator
}
mapping(address => Roles) private userRoles;
constructor() public {
owner = msg.sender;
}
function authorizeUser(address user) public onlyOwner {
authorizedUsers[user] = true;
}
function removeAuthorization(address user) public onlyOwner {
authorizedUsers[user] = false;
}
function setUserRole(address user, Roles role) public onlyOwner {
userRoles[user] = role;
}
function getRecord(bytes32 key) public view onlyAuthorized returns (bytes memory) {
Record storage record = records[key];
Restrictions storage restrictions = accessRestrictions[key];
require(block.timestamp < restrictions.expirationDate, "This record has expired.");
require(uint8(userRoles[msg.sender]) >= restrictions.minRole, "Your role is not high enough to access this record.");
require(restrictions.authorizedUsers[msg.sender], "You are not authorized to access this record.");
return record.data;
}
function setRecord(bytes32 key, bytes memory data, uint256 expirationDate, uint8 minRole, address[] memory authorizedUsers) public onlyOwner {
Record storage record = records[key];
record.data = data;
record.timestamp = block.timestamp;
Restrictions storage restrictions = accessRestrictions[key];
restrictions.expirationDate = expirationDate;
restrictions.minRole = minRole;
for (uint i = 0; i < authorizedUsers.length; i++) {
restrictions.authorizedUsers[authorizedUsers[i]] = true;
}
}
function deleteRecord(bytes32 key) public onlyOwner {
delete records[key];
delete accessRestrictions[key];
}
modifier onlyOwner {
require(msg.sender == owner, "Only the owner can perform this action.");
_;
}
modifier onlyAuthorized {
require(authorizedUsers[msg.sender], "You are not authorized to perform this action.");
_;
}
}
Get more blockchain learning materials through Github!