อีกแค่อาทิตย์เดียว devcon two ที่เป็นงานใหญ่ประจำปีของ Ethereum Foundation ก็จะเริ่มแล้ว เป็นงานที่ Developer และ Researcher จะมาโชว์ความก้าวหน้าล่าสุดกันครับ [agenda]
แม้ว่าโครงการใหญ่ๆ จะเก็บรายละเอียดไว้เปิดตัวในงาน แต่ก็มีโปรเจคเล็กๆ เปิดออกมาให้เห็นกันบ้างแล้วในอาทิตย์นี้ โดยอันหนึ่งที่ผมคิดว่าน่าสนใจคือ FlightDelay ซึ่งเป็นบริการ Travel Insurance ที่พัฒนาขึ้นมาเป็น Ethereum Smart Contract
แนวคิดและวิธ๊การใช้งานของเค้าก็ง่ายมากครับ
ถ้าจะลองทำตามตัวอย่างนี้ ต้องมี “เครื่องมือ” ที่จะติดต่อกับ Ethereum blockchain นะครับ ง่ายที่สุดคือใช้ MetaMask ซึ่งเป็น extension ของ Chrome หรือถ้าอยากลองแบบเต็มๆ ก็สามารถใช้ Mist Browser ได้ครับ แต่จะต้อง synchronize blockchain ครั้งแรกก่อน ซึ่งใช้เวลาหลายชั่วโมง
1. ขั้นแรกก็ไปที่ https://fdd.etherisc.com/ (ใน Mist หรือ Chrome+MetaMask)
2. เลือก flight ที่ต้องการ
ลองเลือก TG662 บินจากกรุงเทพไปเซี่ยงไฮ้วันเสาร์ที่จะถึงนี้ (สมมุติว่าเราจะบินไปร่วมงาน devcon two กับเค้าด้วย)
3. เลือกเบี้ยประกัน
หลังจากเลือก flight แล้ว ตัว smart contract จะไปดึงข้อมูลสถิติการดีเลย์จาก API ของ flightstats.com และบอกจำนวนเงินสินไหมชดเชยให้เราดูว่าพอใจหรือเปล่า
โดยจะมีเหตุการณ์ 5 อย่างซึ่งจ่ายชดเชยไม่เท่ากัน
- Flight ล่าช้า 15 – 29 นาที
- Flight ล่าช้า 30-44 นาที
- Flight ล่าช้า 45 นาทีขึ้นไป
- Flight ถูกยกเลิก
- Flight ถูก divert
โดยจะบอกสถิติในอดีต ว่า Flight ที่เราเลือกมีโอกาสเกิดแต่ละเหตุการณ์มากแค่ไหน และถ้าเกิดขึ้นเราจะได้ค่าชดเชยเท่าไหร่ ซึ่งค่าชดเชยก็ขึ้นอยู่กับเบี้ยประกัน ที่เลือกได้ตั้งแต่ 0.5 ether ถึง 5 ether
ผมเลือกเบี้ยประกัน 1 ether (ราคาวันนี้ประมาณ 400 บาท) เพื่อให้ดูได้ง่ายว่าเงินชดเชยเป็นกี่เท่าครับ จะเห็นว่าเครื่องมีโอกาสดีเลย์ 30-44 นาที เท่ากับ 5% และถ้ามันเกิดขึ้นเราจะได้เงินชดเชย 3.56 ether (ประมาณ 1,400 บาท)
4. Execute smart contract
ขึ้นต่อไปก็คือการ “ทำสัญญา” (execute) ด้วยการส่ง ether จำนวนที่เราต้องการไปที่ FlightDelay smart contract ซึ่งมี contract address อยู่ที่ 0x4D54bE5a62F5d9fcF4b17C7ab6e68822C142ec6B แต่เราไม่ต้องรู้หรอกนะครับ เพราะตัวเว็บจะจัดการให้ ในขั้นตอนนี้จะต้องใส่ password ของ Ethereum wallet ของเรา (ซึ่งต้องทำทุกครั้งที่จะส่ง ether)
5. เรียบร้อย
รอประมาณ 2 นาทีให็ transaction ที่เรา broadcast ออกไปถูก confirm ก็จะเห็นจำนวน ETH ใน wallet ลดลง และ status ของ Policy ของเราขึ้นมาเป็น Applied และ Accepted ในที่สุดครับ
เมื่อถึงเวลาเดินทาง smart contract จะเช็คสถานะของ Flight ว่าออกตรงเวลาหรือช้าแค่ไหน และถ้าเข้าเงื่อนไข 1 ใน 5 ข้อด้านบน ก็จะโอน ether ที่เป็นเงินชดเชยมาให้เราโดยอัตโนมัติครับ
ซึ่งง่ายกว่าการเคลมจากบริษัทประกันมาก!
Smart Contract นี้ทำงานยังไง
การทำสัญญาประกันด้านบนนี้ เราทำกับตัว smart contract โดยตรง ไม่ได้ทำกับบริษัทหรือบุคคลใดๆ ดังนั้นถ้าเราไม่เชื่อใจก็ไม่ควรส่ง ether ไปนะครับ แต่นักพัฒนาส่วนใหญ่ก็เข้าใจเรื่องนี้ จึงมักจะเปิด source code ให้เข้าไปตรวจสอบได้ ก็เป็นโอกาสให้เราเรียนรู้ได้ครับ
Limited Time
ก่อนอื่นเค้าออกตัวไว้ชัดเจน ว่า FlightDelay เป็นแค่ showcase เท่านั้น นะครับ ไม่ได้ตั้งใจจะทำเป็นธุรกิจจริงจัง ดังนั้นเค้าตั้งไว้ว่าให้ทำงานได้จนถึงวันที่ 26 กันยายนนี้เท่านั้น (หลังจบงาน devcon two)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
// Deadline for acceptance of policies: Mon, 26 Sep 2016 12:00:00 GMT
uint contractDeadline = 1474891200;
// don't accept flights with departure time earlier than in 24 hours,
// or arrivalTime before departureTime,
// or departureTime after Mon, 26 Sep 2016 12:00:00 GMT
if (
_arrivalTime < _departureTime ||
_arrivalTime > _departureTime + 2 days ||
_departureTime < now + 24 hours ||
_departureTime > contractDeadline) {
LOG_PolicyDeclined(0, 'Invalid arrival/departure time');
if (!msg.sender.send(msg.value)) {
LOG_SendFail(0, 'newPolicy sendback failed (2)');
}
return;
}
|
Underwriting – Share the Risk
ไอเดียของ FlightDelay ก็เป็นไอเดียพื้นฐานของการทำประกันนั่นเองครับ คือการแชร์ความเสี่ยง โดยคนจำนวนมากทำประกัน แต่ละคนจ่ายเบี้ยประกันนิดหน่อย ถ้าเกิดเหตุการณ์ไม่ดีขึ้นกับบางคนในนั้น ก็จะได้รับการชดเชยจากเบี้ยประกันที่เอามารวมกัน ทุกคนมีค่าใช้จ่ายนิดหน่อย แต่จะไม่มีใคร suffer มาก
เบี้ยประกันที่ได้จะถูกหัก 3% ให้นักพัฒนาและไว้รองรับ tail risks
1
2
3
4
|
// 1 percent for DAO, 1 percent for maintainer
uint8 constant rewardPercent = 2;
// reserve for tail risks
uint8 constant reservePercent = 1;
|
เวลาเรา execute contract เบี้ยประกัน (premium) ก็จะถูกหักไว้เป็น reward และ reserve ก่อน
1
2
3
4
5
6
7
8
9
10
11
12
|
function bookAndCalcRemainingPremium() internal returns(uint) {
uint v = msg.value;
uint reserve = v * reservePercent / 100;
uint remain = v - reserve;
uint reward = remain * rewardPercent / 100;
bookkeeping(acc_Balance, acc_Premium, v);
bookkeeping(acc_Premium, acc_RiskFund, reserve);
bookkeeping(acc_Premium, acc_Reward, reward);
return (uint(remain - reward));
}
|
ตอนจะ underwrite กรมธรรม์ใหม่ smart contract จะเช็คข้อมูลที่ผ่านมาของ Flight ที่เราเลือก แล้วนับจำนวนครั้งที่เคยเกิดการดีเลย์หรือยกเลิกเที่ยวบิน (ตัวแปร statistics[i])
(ส่วนต่อไปนี้รายละเอียดเยอะ ผมตัดมาเฉพาะส่วนหลักนะครับ ถ้าสนใจสามารถดูฉบับเต็มได้ที่นี่)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
function callback_ForUnderwriting(uint _policyId, string _result, bytes _proof)
onlyInState(_policyId, policyState.Applied)
internal {
var sl_result = _result.toSlice();
risk r = risks[policies[_policyId].riskId];
// we expect result to contain 6 values, something like
// "[61, 10, 4, 3, 0, 0]" ->
// ['observations','late15','late30','late45','cancelled','diverted']
uint observations = parseInt(
sl_result.split(', '.toSlice()).toString());
// calculate statistics (scaled by 10000; 1% => 100)
statistics[0] = observations;
for (uint i = 1; i <= 5; i++) {
statistics[i] =
parseInt(
sl_result.split(', '.toSlice()).toString()) * 10000 / observations;
}
// underwrite policy
underwrite(_policyId, statistics, _proof);
}
|
หลังจากนั้นก็จะเอา statistics
มาคำนวณเป็นความน่าจะเป็น (ตัวแปร weight
)
และกำหนดเงินชดเชย (ตัวแปร payout
) จาก premium
และ weight
(ยิ่ง Flight นี้เคยดีเลย์บ่อย weight
ก็จะยิ่งเยอะ และ payout
ก็จะยิ่งน้อยลง)
1
2
3
4
5
6
7
8
9
10
11
|
function underwrite(uint _policyId, uint[6] _statistics, bytes _proof) internal {
policy p = policies[_policyId]; // throws if _policyId invalid
uint weight;
for (uint8 i = 1; i <= 5; i++) {
weight += weightPattern[i] * _statistics[i];
// 1% = 100 / 100% = 10,000
}
p.weight = weight;
p.state = policyState.Accepted;
p.stateMessage = 'Policy underwritten by oracle';
}
|
การจ่ายค่าชดเชย
ส่วนนี้ตัว smart contract จะตรวจสอบสถานะของ Flight และดึงเอาระยะเวลาที่ดีเลย์มา
ถ้า delay
= 0 ก็ถือว่าจบสัญญาโดยไม่ต้องจ่ายค่าชดเชย
ถ้า delay
มากกว่า 0 ก็จะดูว่าเข้ากลุ่มเหตุการณ์ไหน, คำนวณ payout
, และทำการจ่ายเงิน
โดยจะมี check ต่างๆ ก่อนจ่าย 2-3 อย่าง เช่น
- จะจ่ายไม่เกิน
maxPayout
(ตอนนี้ตั้งไว้ที่ 150 ether) - เช็คก่อนว่ามีเงินเหลือพอจ่าย ก่อนจะพยายามจ่าย
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
function payOut(uint _policyId, uint8 _delay, uint _delayInMinutes)
notInMaintenance
onlyOraclize
onlyInState(_policyId, policyState.Accepted)
internal {
policy p = policies[_policyId];
risk r = risks[p.riskId];
r.delay = _delay;
r.delayInMinutes = _delayInMinutes;
if (_delay == 0) {
p.state = policyState.Expired;
p.stateMessage = 'Expired - no delay!';
p.stateTime = now;
LOG_PolicyExpired(_policyId);
} else {
uint payout = p.premium * weightPattern[_delay] * 10000 / p.weight;
p.calculatedPayout = payout;
if (payout > maxPayout) {
payout = maxPayout;
}
if (payout > uint(-ledger[acc_Balance])) { // don't go for chapter 11
payout = uint(-ledger[acc_Balance]);
}
p.actualPayout = payout;
bookkeeping(acc_Payout, acc_Balance, payout); // cash out payout
p.state = policyState.PaidOut;
p.stateMessage = 'Payout successful!';
p.stateTime = now; // won't be reverted in case of errors
LOG_PolicyPaidOut(_policyId, payout);
}
}
|
Oracle
เวลา smart contract ต้องใช้ข้อมูลของโลกภายนอกเพื่อประกอบการทำงานของตัวเอง แหล่งข้อมูลที่ smart contract เชื่อถือพวกนี้เรียกว่า Oracle (คนละอย่างกับบริษัท IT ยักษ์ใหญ่นะครับ)
Oracle ที่ FlightDelay ใช้ก็คือ API ของ FlightStats โดยในส่วนการติดต่อกับ Oracle นี้เค้าใช้โค้ดของ Oraclize
ป้องกัน wrong incentive
ถ้ามีคนใช้งานเยอะๆ อาจมีเหตุการณ์ที่ทำให้เกิด incentive ที่ผิดๆ ได้ เช่นถ้ามีคนจำนวนมากบน Flight เดียวกันทำประกันเครื่อง Delay ไว้ อาจทำให้เกิดแรงจูงใจที่จะช่วยกันทำให้เครื่องดีเลย์เพื่อรับค่าชดเชย ส่วนนี้ของ code มีไว้เพื่อป้องกันเหตุการณ์นี้ ด้วยการกำหนดไม่รับการประกัน “ความเสี่ยงเดียวกัน” มากเกินค่าค่าหนึ่ง ซึ่งความเสี่ยงเดียวกันนี้ก็คือการประกัน Flight เดียวกันนั่นเอง โดยเค้าสร้างตัวแปร riskId
ขึ้นมาจาก
- หมายเลข Flight
- วันเดือนปีที่ออกเดินทาง
- Arrival Time
ถ้ามีคนพยายามจะทำประกันเพิ่ม แต่ riskId
นั้นมีมูลค่าประกันเกิน maxCumulatedWeightedPremium
ตัว smart contract ก็จะไม่ยอมให้ทำ และ return error message “Cluster Risk”
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
// accept only a number of maxIdenticalRisks identical risks:
bytes32 riskId = sha3(
_carrierFlightNumber,
_departureYearMonthDay,
_arrivalTime
);
risk r = risks[riskId];
// roughly check, whether maxCumulatedWeightedPremium will be exceeded
// (we accept the inaccuracy that the real remaining premium is 3% lower),
// but we are conservative;
// if this is the first policy, the left side will be 0
if (msg.value * r.premiumMultiplier + r.cumulatedWeightedPremium >=
maxCumulatedWeightedPremium) {
LOG_PolicyDeclined(0, 'Cluster risk');
if (!msg.sender.send(msg.value)) {
LOG_SendFail(0, 'newPolicy sendback failed (3)');
}
return;
}
|
ภาพสนามบินกัลกัตตา โดย Yuvipanda