forked from etherisc-legacy/govhack
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSocialInsurance.sol
217 lines (190 loc) · 6.52 KB
/
SocialInsurance.sol
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
/*
Social Insurance -
Proposal for Blockchain UAE GovHack Competition
(C) 2016 etherisc.com
@Author Christoph Mussenbrock
*/
pragma solidity ^0.4.7;
contract SocialInsurance {
modifier onlyOwner {
if (msg.sender != owner) {
throw;
}
_;
}
event LOG_memberPaidPremium(address _member, uint _value);
uint8 public constant MAX_MEMBERS = 12;
uint8 public constant MAX_LEVEL = 4;
uint8 public constant LOCAL_LEVEL = 1;
uint8 public constant NONE = 0x0;
uint public constant WAIT_BLOCKS = 0; // could be 4 * 60 * 24 * 180; // blocks to wait till first payout
uint public constant WAIT_NEXT_PAYOUT_BLOCKS = 0; // some time to pass between succeeding payouts
/**
* A peergroup is either a local group of members (level == 0)
* or a group of groups (level > 0).
* A peergroup is identified by the address of its spokesperson.
*/
struct Peergroup {
address parentGroup;
string group_name;
string spokesperson_name;
string spokesperson_contact;
uint balance;
uint payouts;
uint8 level;
uint8 numberOfMembers;
uint lastPayout; // block number of last payout;
}
/**
* A member has a group (identified by its spokesperson)
* a balance (sum of all paid premiums) and
* a payouts (sum of all payouts)
*/
struct Membership {
address group_spokesperson;
uint joinedAtBlock;
uint balance;
uint payouts;
uint joinedTime;
}
address public owner;
address public rootSpokesperson; // spokesperson of top-level group
uint public maxPayout;
uint public premium_propagate_divisor;
uint public payouts_propagate_divisor;
mapping(address => Peergroup) public groups; // mapping of spokespersons to groups
mapping(address => Membership) public members; // mapping of members to spokespersons
mapping(address => address[]) public group_members; //
function SocialInsurance() {
owner = msg.sender;
maxPayout = 500 ether;
premium_propagate_divisor = 2;
payouts_propagate_divisor = 2;
}
function createTopGroup (address _root_spokesperson, string _group_name, string _spokesperson_name, string _spokesperson_contact)
onlyOwner {
if (rootSpokesperson != 0x0) {
throw;
}
Peergroup topGroup = groups[_root_spokesperson];
topGroup.parentGroup = _root_spokesperson; // only in topGroup
topGroup.group_name = _group_name;
topGroup.spokesperson_name = _spokesperson_name;
topGroup.spokesperson_contact = _spokesperson_contact;
topGroup.level = MAX_LEVEL;
rootSpokesperson = _root_spokesperson;
}
function createGroup(address _spokesperson, string _group_name, string _spokesperson_name, string _spokesperson_contact) {
Peergroup group = groups[msg.sender];
// Sanity check:
if (group.parentGroup == NONE // group doesn't exist
|| group.level == LOCAL_LEVEL // local groups can't have subgroups
|| group.numberOfMembers >= MAX_MEMBERS) { // group is full
throw;
}
// create new group and assign to _elder's group
group.numberOfMembers++;
Peergroup childGroup = groups[_spokesperson];
if (childGroup.parentGroup != NONE) { // spokesperson must be unique
throw;
}
childGroup.parentGroup = msg.sender;
childGroup.level = group.level - 1;
childGroup.group_name = _group_name;
childGroup.spokesperson_name = _spokesperson_name;
childGroup.spokesperson_contact = _spokesperson_contact;
group_members[msg.sender].push(_spokesperson);
// other fields don't need initialization.
}
function setParameter(uint _new_maxPayout, uint _new_premium_propagate_divisor, uint _new_payouts_propagate_divisor)
onlyOwner {
maxPayout = _new_maxPayout;
premium_propagate_divisor = _new_premium_propagate_divisor;
payouts_propagate_divisor = _new_payouts_propagate_divisor;
}
function isMember(address _member) constant returns (bool) {
return members[_member].group_spokesperson != NONE;
}
function admitMember (address _member) {
Peergroup group = groups[msg.sender];
if (isMember(_member) // Already member.
|| group.parentGroup == NONE // group doesn't exist
|| group.level > LOCAL_LEVEL // not a local group
|| group.numberOfMembers >= MAX_MEMBERS) // group is full
{
throw;
}
group.numberOfMembers++;
members[_member].group_spokesperson = msg.sender;
members[_member].joinedAtBlock = block.number;
members[_member].joinedTime = now;
group_members[msg.sender].push(_member);
}
function propagatePremium(address _spokesperson, uint _premium) {
Peergroup group = groups[_spokesperson];
if (group.level == MAX_LEVEL) {
group.balance += _premium;
return;
}
uint remain = _premium / premium_propagate_divisor;
uint propagate = _premium - remain;
group.balance += remain;
propagatePremium(group.parentGroup, propagate);
}
function () payable {
if (!isMember(msg.sender)) {
throw;
}
address spokesperson = members[msg.sender].group_spokesperson;
Peergroup group = groups[spokesperson];
members[msg.sender].balance += msg.value;
propagatePremium(spokesperson, msg.value);
LOG_memberPaidPremium(msg.sender, msg.value);
}
function propagatePayout(address _spokesperson, uint _payout) returns (uint) {
Peergroup group = groups[_spokesperson];
uint actual_payout = 0;
if (group.level == MAX_LEVEL) {
if (group.balance > _payout) {
actual_payout = _payout;
} else {
actual_payout = group.balance;
}
group.balance -= actual_payout;
group.payouts += actual_payout;
return actual_payout;
}
uint remain = _payout / payouts_propagate_divisor;
if (group.balance > remain) {
actual_payout = remain;
} else {
actual_payout = group.balance;
}
group.balance -= actual_payout;
group.payouts += actual_payout;
actual_payout = actual_payout + propagatePayout(group.parentGroup, _payout - actual_payout);
return actual_payout;
}
function payout(address _member, uint _payout) {
if (!isMember(_member) || _payout > maxPayout) {
throw;
}
if (members[_member].joinedAtBlock + WAIT_BLOCKS > block.number) {
throw;
}
address spokesperson = members[_member].group_spokesperson;
if (spokesperson != msg.sender) {
throw;
}
Peergroup group = groups[spokesperson];
uint actual_payout = propagatePayout(spokesperson, _payout);
if (group.lastPayout + WAIT_NEXT_PAYOUT_BLOCKS > block.number) {
throw;
}
members[_member].payouts += actual_payout;
group.lastPayout = block.number;
if (!_member.send(actual_payout)) {
throw;
}
}
}