前排提醒:该案例完全实现需要分为七个智能合约,且每个智能合约代码之间的内在罗辑以及各个智能合约之间的联系都已经详细说明。相信全部看完的你会对智能合约有更深入的了解。
合约总体介绍:现在利用区块链技术实现航班延误险系统,将乘机人、航空公司、保险公司加入到区块链网络中,将购买、航班、保险、保单等信息存储在区块链的分布式网络中,永久有效,无法篡改。在机票延误险场景中,乘机人购买机票之后在系统上预存10元保费;保险公司再在系统上预存相应的赔偿金1000元;如果保险公司没有按时预存赔偿金,系统就直接将保费退还给用户;如果保险公司预存了赔偿金,若航班没有延误,或者延误时间少于4h,系统就将乘机人预存的保费转账给保险公司,同时退还保险公司预存的赔偿金。如果航班延误超过4h,系统也会将保费转账给保险公司,但是会将保险公司预存的赔偿金赔偿给乘机人。
首先根据需求为以下角色分别创建合约,并实现一些功能。
1.Roles.sol 账户合约
(主要为后面的乘客A,航空公司V,保险公司C提供环境)
1.查询添加的账户是否为空
2.增加、删除账户。
// SPDX-License-Identifier: 3.0
pragma solidity ^0.8.20;
library Roles {
struct Role {
mapping (address => bool) bearer;
}
function has(Role storage role, address account) internal view returns (bool) {
require(account != address(0), "Roles: account is the zero address");
return role.bearer[account];
}
//查询添加的账户是否为空
function add(Role storage role, address account) internal {
require(!has(role, account), "Roles: account already has role");
role.bearer[account] = true;
}
//增加账户
function remove(Role storage role, address account) internal {//
require(has(role, account), "Roles: account does not have role");
role.bearer[account] = false;
}
//删除账户
}
2.AirlineV.sol航空公司合约
首先声明两个事件(增删账户)=》
初始化合约(构造函数)=》
创建修饰器 onlyairlineV(限制访问权限时调用)=》
构造isairlineV函数判断是否具有airlineV角色=》
构造两个添加航空公司的函数,用于合约内部调用(_addairlineV)和外部调用(addairlineV)=》
构造一个合约内部删除函数(_removeairlineV)和用户自行退出函(renounceairlineV)
注意:内部调用需触发刚开始定义的事件,外部调用需要增加onlyairlineV权限限制。(下同)
// SPDX-License-Identifier: 3.0
pragma solidity ^0.8.20;
import "./Roles.sol";
contract AirlineV {
using Roles for Roles.Role;
Roles.Role private _airlineV;
//将_airlineV声明为私有变量
event airlineVAdded(address account);
event airlineVRemoved(address account);
constructor (address airlineV) {
_addairlineV(airlineV);
}
modifier onlyairlineV() {
require( isairlineV(msg.sender),"airlineVRole: caller does not have the airlineV role");
_;
}
//修饰符中使用了 isairlineV(msg.sender) 函数来检查调用者是否具有 airlineV 角色
function isairlineV(address account)public view returns(bool){
return _airlineV.has(account);
}
function _addairlineV(address account) internal {
_airlineV.add(account);
emit airlineVAdded(account);
}
function addairlineV(address account) public onlyairlineV{
_addairlineV(account);
}
//将制定用户添加到航空公司,只有航空公司的人可以调用
function _removeairlineV(address account) internal {
// _ai,.rlineV.remove(account);
emit airlineVRemoved(account);
}
function renounceairlineV() public {
_removeairlineV(msg.sender);
}
}
3..InsuranceCompanyC.sol保险公司合约
原理同上,代码也相似
// SPDX-License-Identifier: 3.0
pragma solidity ^0.8.20;
import "./Roles.sol";
//保险公司C角色
contract InsuranceCompanyC {
using Roles for Roles.Role;
Roles.Role private _insuranceCompanyC;
event insuranceCompanyCAdded(address account);
event insuranceCompanyCRemoved(address account);
//声明两个事件,增加和删除账号。
constructor (address insuranceCompanyC){
_addInsuranceCompanyC(insuranceCompanyC);
}
modifier onlyInsuranceCompanyC() {
require(isInsuranceCompanyC(msg.sender), "insuranceCompanyCRole: caller does not have the insuranceCompanyC role");
_;
}
//只有内部账号可以调用
function isInsuranceCompanyC(address account)public view returns(bool){
return _insuranceCompanyC.has(account);
}
function _addInsuranceCompanyC(address account) internal {
_insuranceCompanyC.add(account);
emit insuranceCompanyCAdded(account);
}
function addinsuranceCompanyC(address account) public onlyInsuranceCompanyC{
_addInsuranceCompanyC(account);
}
//只能保险公司的人可以调用
function _removeinsuranceCompanyC(address account) internal{
_insuranceCompanyC.remove(account);
emit insuranceCompanyCRemoved(account);
}
function renounceinsuranceCompanyC() public{
_removeinsuranceCompanyC(msg.sender);
}
}
4..PassengerA.sol乘客合约
原理同上,代码也相似。
// SPDX-License-Identifier: 3.0
pragma solidity ^0.8.20;
import "./Roles.sol";
//乘客A角色
contract PassengerA {
using Roles for Roles.Role;
Roles.Role private _passengerA;
event passengerAAdded(address account);
event passengerARemoved(address account);
modifier onlypassengerA() {
require(ispassengerA(msg.sender), "passengerARole: caller does not have the passengerA role");
_;
}
function ispassengerA(address account) public view returns (bool) {
return _passengerA.has(account);
}
function _addpassengerA(address account) internal {
_passengerA.add(account);
emit passengerAAdded(account);
}
function addpassengerA(address account) public {
_addpassengerA(account);
}
function _removepassengerA(address account) internal {
_passengerA.remove(account);
emit passengerARemoved(account);
}
function renouncepassengerA() public {
_removepassengerA(msg.sender);
}
}
(五)FlightManagement.sol飞行管理合约
定义 航班延误险信息,航班信息,乘客信息三个结构体,包含飞行管理的各种信息。=》
创建三个映射(包含信息结构体)通过调用flights, passengers, passengerNumber映射,获取其相对应的信息。=》
正式开始,初始化设置(构造函数)(可以使用其他合约的指定地址)=》
添加航班。
1.通过 映射flights[_flightNumber]修改定航班号对应的航班信息
。2. 利用结构体FlightStorage初始化相关数据。=》
同理添加乘客信息(addPassengerInfo)=》
更新实际起飞时间,(利用.actualDepartureTime 字段来更新)。=》
同理更新实际到达时间。注意:以上添加航班和更新实际起飞时间需要补充代码,其可以通过对照来完成。=》
获取航班信息。
1.构造函数getFlight(接受_flightNumber参数)并返回一个包含航班信息的元组
2.访问flights映射中的航班信息,赋值给名为flight的结构体FlightStorage。
3.返回flight中的值给元组。=》
同理获取乘客信息(需补充,与前面对照)=》
乘客是否购买机票。
1.检查航班号,返回bool值。
2.for循环查询身份证进行验证( keccak256(abi.encodePacked(...)方法)=》
利用映射定义购买机票的时间=》
乘客购买机票。
1.同上。
2.进行三轮判断(是否已经起飞,是否已经上传信息,是否已经购买航班的机票)。
3.将当前乘客添加到航班中(先将乘客地址与航班号关联起来,在将乘客添加进数组(push)记录每个用户购买机票的时间。)=》
新增座位号,
1.同上
2.判断乘客是否购买机票,
3.for循环查询身份证进行验证。
// SPDX-License-Identifier: 3.0
pragma solidity ^0.8.20;
import "./InsuranceCompanyC.sol";
import "./AirlineV.sol";
import "./PassengerA.sol";
contract FlightManagement is InsuranceCompanyC, AirlineV, PassengerA{
address airlineV; // 航空公司V的地址
address insuranceCompanyC; // 保险公司C的地址
// 航班延误险信息
struct InsuranceStatus {
//待补充
uint premium; //保险费
uint compensation; //赔偿金额
uint InsurancePurchaseTime; //购买保险的时间
uint depositTime; //存入赔偿金额的时间
bool purchased; //是否购买保险
bool deposited; //是否存入了赔偿金额
bool insured; //是否退保
//补充完成
bool policy; //是否生成保单
string policyContent; // 保单内容
}
// 航班信息
struct FlightStorage {
string flightNumber; //航班号
uint scheduledDepartureTime; //计划起飞时间
uint actualDepartureTime; //实际起飞时间
uint scheduledArrivalTime; //预计到达时间
uint actualArrivalTime; //实际到达时间
bool delayed; //是否延误
InsuranceStatus insuranceStatus; //航班延误险信息
}
// 乘客信息
struct PassengerStorage {
string idNumber; // 身份证号码
string name; // 姓名
string gender; // 性别
uint birthDate; // 出生年月
string seatNumber; // 座位号
}
mapping(string => FlightStorage) flights;// 航班号到航班信息
mapping(address => PassengerStorage) passengers; // 地址对应的乘客信息
mapping(string => PassengerStorage[]) passengerNumber;// 航班号到乘客
constructor(address _insuranceCompanyC, address _airlineV) InsuranceCompanyC(_insuranceCompanyC) AirlineV(_airlineV){
airlineV = _airlineV;
insuranceCompanyC = _insuranceCompanyC;
}
// 添加航班
function addFlight(string memory _flightNumber, uint _scheduledDepartureTime, uint _scheduledArrivalTime, uint _premium, uint _compensation) public onlyairlineV{
flights[_flightNumber] = FlightStorage(_flightNumber, _scheduledDepartureTime, 0, _scheduledArrivalTime, 0, false,
InsuranceStatus(_premium, _compensation, 0, 0, false, false, false, false, ""));
}
// 添加乘客信息
function addPassengerInfo(string memory _idNumber, string memory _name, string memory _gender, uint _birthDate) public onlypassengerA{
passengers[msg.sender] = PassengerStorage(_idNumber, _name, _gender, _birthDate, "");
}
// 更新实际起飞时间
function updateDepartureTime(string memory _flightNumber, uint _actualDepartureTime) public onlyairlineV{
flights[_flightNumber].actualDepartureTime = _actualDepartureTime;
}
// 更新实际到达时间
function updateArrivalTime(string memory _flightNumber, uint _actualArrivalTime) public onlyairlineV{
flights[_flightNumber].actualArrivalTime = _actualArrivalTime;
}
// 获取航班信息
function getFlight(string memory _flightNumber) public view returns (string memory, uint, uint, uint, uint, uint, uint) {
FlightStorage memory flight = flights[_flightNumber];
return (flight.flightNumber, flight.scheduledDepartureTime, flight.actualDepartureTime, flight.scheduledArrivalTime, flight.actualArrivalTime,
flight.insuranceStatus.premium, flight.insuranceStatus.compensation);
}
// 获取乘客信息
function getPassenger() public view returns (string memory, string memory, string memory, uint) {
PassengerStorage memory passenger = passengers[msg.sender];
return (passenger.idNumber, passenger.name, passenger.gender, passenger.birthDate);
}
// 乘客是否购买机票
function _hasPurchasedTicket(string memory _flightNumber) internal view returns (bool) {
for (uint i = 0; i < passengerNumber[_flightNumber].length; i++) {
if (keccak256(abi.encodePacked(passengerNumber[_flightNumber][i].idNumber)) == keccak256(abi.encodePacked(passengers[msg.sender].idNumber))) { //
// 比较航班里是否存在身份证号相同的乘客
return true;
}
}
return false;
}
uint TicketPurchaseTime;
// 购买机票
function addPassengerToFlight(string memory _flightNumber) public onlypassengerA {
require(flights[_flightNumber].scheduledDepartureTime > block.timestamp, "The flight has already departed");// 判断航班是否已经起飞
require(passengers[msg.sender].birthDate != 0, "Please upload your passenger information first");// 判断乘客是否已经上传信息
require(!_hasPurchasedTicket(_flightNumber), "You have already purchased a ticket for this flight");// 判断乘客是否已经购买航班的机票
//将当前乘客添加到航班中,将该乘客的地址与航班号关联起来
PassengerStorage memory passenger = passengers[msg.sender];
passengerNumber[_flightNumber].push(passenger);
passengers[msg.sender] = passenger;
//定义一个状态变量记录购买机票的时间
TicketPurchaseTime = block.timestamp;
}
// 新增座位号
function addSeatNumber(string memory _flightNumber, string memory _seatNumber) public onlypassengerA{
require(_hasPurchasedTicket(_flightNumber), "You have not purchased a ticket for this flight");// 判断乘客是否已经购买航班的机票,必须购买
for (uint i = 0; i < passengerNumber[_flightNumber].length; i++) {
if (keccak256(abi.encodePacked(passengerNumber[_flightNumber][i].idNumber)) == keccak256(abi.encodePacked(passengers[msg.sender].idNumber))) {
// 比较航班里是否存在身份证号相同的乘客
passengerNumber[_flightNumber][i].seatNumber = _seatNumber;//新增座位号
}
}
}
}
由于篇幅原因,后两个个主要实现功能的航班延误险合约和索赔合约合约将放在下篇。