Надежно храните и делитесь своими медицинскими записями с помощью смарт-контрактов

Использование блокчейна для общедоступных данных: сложный вопрос

Вопрос о том, подходит ли блокчейн для общедоступных данных, сложен, и не существует универсального решения. Тем не менее, есть несколько возможных решений, которые можно было бы рассмотреть:

  1. Частная сеть. Одним из решений проблем конфиденциальности, связанных с технологией блокчейна, является использование частного блокчейна вместо общедоступного. Частные блокчейны могут обеспечить преимущества технологии блокчейн, такие как неизменность и прозрачность, а также обеспечить конфиденциальность и безопасность данных.
  2. Гибридный подход. Другой вариант — использовать гибридный подход, при котором конфиденциальные данные хранятся в частной цепочке блоков, а неконфиденциальные данные хранятся в общедоступной цепочке блоков. Это позволяет вам пользоваться преимуществами обоих типов блокчейна, обеспечивая при этом безопасность конфиденциальной информации.
  3. Многоуровневая архитектура. Многоуровневую архитектуру можно использовать для отделения конфиденциальных данных от неконфиденциальных данных, обеспечивая безопасное хранение конфиденциальной информации, в то время как неконфиденциальные данные могут храниться в общедоступной цепочке блоков.
  4. Шифрование: Шифрование можно использовать для обеспечения безопасности конфиденциальных данных. Это может обеспечить дополнительный уровень безопасности и защитить от несанкционированного доступа к конфиденциальной информации.

Разработка смарт-контрактов

Определите переменные, используемые для хранения данных о владельце контракта, медицинских записях, авторизованных пользователях и ограничениях доступа к каждой записи. Переменные records и accessRestrictions представляют собой сопоставления, хранилища ключей и значений, в которых ключи и значения могут быть любого типа.

Отображение records сопоставляет ключ типа bytes32 со значением типа Record, которое является структурой, определенной позже в контракте. Отображение authorizedUsers сопоставляет адрес с логическим значением, указывающим, может ли пользователь получать доступ к записям. Отображение accessRestrictions сопоставляет ключ bytes32 со значением типа Restrictions, которое также является структурой, определенной позже в контракте.

pragma solidity ^0.7.0;
// It is important to thoroughly review and test any smart contract before using it.

contract MedicalRecord {
    // The address of the owner of the contract.
    address private owner;
    // The medical records that are being stored.
    mapping(bytes32 => Record) private records;
    // The authorized users who are allowed to access the records.
    mapping(address => bool) private authorizedUsers;
    // The restrictions on access to each record.
    mapping(bytes32 => Restrictions) private accessRestrictions;

Ниже данные строки определяют структуры Record и Restrictions, которые используются для хранения данных для одной медицинской карты и ограничения доступа к одной карте соответственно. Структура Record имеет два поля: data, значение bytes, в котором хранятся фактические данные медицинской карты, и timestamp, значение uint256, в котором хранится время добавления или последнего обновления записи. Структура Restrictions имеет три поля: authorizedUsers — сопоставление, в котором хранятся пользователи, которым разрешен доступ к записи, minRole — значение uint8, в котором хранится минимальная роль, необходимая для доступа к записи, и expirationDate — поле uint256. значение, в котором хранится дата истечения срока действия записи.

// The roles that a user can have.
    // The data for a single medical record.
    struct Record {
        bytes data;
        uint256 timestamp;
    }

    // The restrictions on access to a single record.
    struct Restrictions {
        // The users who are allowed to access the record.
        mapping(address => bool) authorizedUsers;
        // The minimum role required to access the record.
        uint8 minRole;
        // The expiration date for the record.
        uint256 expirationDate;
    }

Теперь определите тип enum (перечисление) с именем Roles, который используется для представления различных ролей, которые может иметь пользователь. enum имеет три возможных значения: Patient, Doctor и Administrator.

// The roles that a user can have.
    enum Roles {
        Patient,
        Doctor,
        Administrator
    }

Создайте имя сопоставления userRoles, которое сопоставляет адрес со значением типа Roles, которое enum определено ранее. Это сопоставление используется для хранения роли каждого пользователя. Вот как это выглядит:

// The role of each user.
    mapping(address => Roles) private userRoles;

Создайте конструктор контракта, который вызывается при развертывании контракта. Конструктор устанавливает переменную owner в адрес пользователя, развертывающего контракт. Вот как это сделать:

// The constructor sets the owner of the contract.
    constructor() public {
        owner = msg.sender;
    }

Теперь создайте функции, позволяющие владельцу контракта авторизовать или отозвать авторизацию пользователя для доступа к записям и установить роль пользователя. Отображение authorizedUsers обновляется, чтобы отразить изменения в статусе авторизации. Отображение userRoles также обновляется, чтобы отразить изменения роли. Вот как это выглядит:

// Only the owner can authorize a user to access records.
    function authorizeUser(address user) public onlyOwner {
        authorizedUsers[user] = true;
    }

    // Only the owner can remove a user's authorization to access records.
    function removeAuthorization(address user) public onlyOwner {
        authorizedUsers[user] = false;
    }

    // Only the owner can set the role of a user.
    function setUserRole(address user, Roles role) public onlyOwner {
        userRoles[user] = role;
    }

Теперь создайте функцию, которая позволит авторизованным пользователям получать медицинскую карту. Функция принимает на вход ключ типа bytes32, который используется для идентификации записи. Функция сначала извлекает запись и ее ограничения из хранилища, используя сопоставления records и accessRestrictions. Затем функция проверяет, не предшествует ли текущее время дате истечения срока действия записи, равна ли роль пользователя минимальной роли, необходимой для доступа к записи, или выше, и имеет ли пользователь право доступа к записи.

Если какое-либо из этих условий не выполняется, функция выдает ошибку. Функция возвращает данные для записи, если все условия соблюдены. Вот что вы можете увидеть:

// Only authorized users can access a record if the current time is before the record's expiration date
    // and the user's role is equal to or higher than the minimum role required to access the record.
    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 позволяет владельцу добавлять или обновлять медицинскую карту. Функция принимает ключ типа bytes32 и данные для записи в качестве входных данных и дату истечения срока действия. Это выглядит так:

// Only the owner can add or update a record.
    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 позволяет владельцу удалить медицинскую карту. Функция принимает на вход ключ типа bytes32 и удаляет запись и ее ограничения из хранилища с помощью ключевого слова delete. Вот как это сделать:

// Only the owner can delete a record.
    function deleteRecord(bytes32 key) public onlyOwner {
        delete records[key];
        delete accessRestrictions[key];
    }

Следующие строки определяют две функции modifier: onlyOwner и onlyAuthorized. modifier — это функция, которую можно использовать для изменения поведения других функций. Модификатор onlyOwner можно применить к функции, чтобы ограничить вызов функции только владельцем контракта.

Модификатор onlyAuthorized можно применить к функции, чтобы ограничить вызов функции только авторизованными пользователями. Функции modifier используют ключевое слово require для принудительного применения ограничений. Если условия, указанные в операторе require, не выполняются, функция выдает ошибку.

// Only the owner can call these functions.
    modifier onlyOwner {
        require(msg.sender == owner, "Only the owner can perform this action.");
        _;
    }

    // Only authorized users can call these functions.
    modifier onlyAuthorized {
        require(authorizedUsers[msg.sender], "You are not authorized to perform this action.");
        _;
    }
}

Полный код

Этот контракт позволяет владельцу хранить медицинские записи, устанавливать ограничения на доступ к каждой записи и разрешать пользователям доступ к записям. Ограничения включают дату истечения срока действия, минимальную требуемую роль и список авторизованных пользователей. Контракт также имеет сопоставление для хранения роли каждого пользователя.

Внешний интерфейс и полный код доступны на GitHub:https://github.com/ac12644/Medical-Records.git