-
Notifications
You must be signed in to change notification settings - Fork 30
/
MessageTribute.sol
185 lines (166 loc) · 5.69 KB
/
MessageTribute.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
pragma solidity >=0.5.0 <0.6.0;
import "../token/ERC20Token.sol";
import "../common/MessageSigned.sol";
/**
* @title MessageTribute
* @author Richard Ramos (Status Research & Development GmbH)
* @dev Inspired by one of Satoshi Nakamoto’s original suggested use cases for Bitcoin,
we will be introducing an economics-based anti-spam filter, in our case for
receiving messages and “cold” contact requests from users.
token is deposited, and transferred from stakeholders to recipients upon receiving
a reply from the recipient.
*/
contract MessageTribute is MessageSigned {
event AudienceGranted(address indexed from, address indexed to, bool approve);
struct Fee {
uint256 amount;
bool permanent;
}
mapping(bytes32 => bool) private granted;
mapping(address => mapping(address => Fee)) public feeCatalog;
mapping(address => mapping(address => uint)) private lastAudienceDeniedTimestamp;
ERC20Token public token;
/**
* @notice Contructor of MessageTribute
* @param _token Address of Status Network Token (or any ERC20 compatible token)
**/
constructor(ERC20Token _token) public {
token = _token;
}
/**
* @notice Set tribute for accounts or everyone
* @param _to Address to set the tribute. If address(0), applies to everyone
* @param _amount Required tribute amount (using token from constructor)
* @param _isPermanent Tribute applies for all communications on only for the first
*/
function setRequiredTribute(address _to, uint _amount, bool _isPermanent) public {
feeCatalog[msg.sender][_to] = Fee(_amount, _isPermanent);
}
/**
* @notice Obtain amount of tokens required from `msg.sender` to contact `_from`
* @return fee amount of tokens
*/
function getRequiredFee(address _from) public view
returns (uint256 fee)
{
Fee memory f = getFee(_from, msg.sender);
fee = f.amount;
}
/**
* @notice Approve/Deny chat request to `_to`
* @param _approve Approve or deny request
* @param _waive Refund deposit or not
* @param _secret Captcha solution
* @param _timeLimit time limit of audience request
* @param _requesterSignature signature of Audience requestor
* @param _grantorSignature signature of Audience grantor
*/
function grantAudience(
bool _approve,
bool _waive,
bytes32 _secret,
uint256 _timeLimit,
bytes calldata _requesterSignature,
bytes calldata _grantorSignature
) external {
require(_timeLimit <= block.timestamp);
address grantor = recoverAddress(
getSignHash(
getGrantAudienceHash(
keccak256(_requesterSignature),
_approve,
_waive,
_secret
)
),
_grantorSignature
);
bytes32 hashedSecret = keccak256(abi.encodePacked(grantor, _secret));
require(!granted[hashedSecret]);
granted[hashedSecret] = true;
address requester = recoverAddress(
getSignHash(
getRequestAudienceHash(
grantor,
hashedSecret,
_timeLimit
)
),
_requesterSignature
);
require(lastAudienceDeniedTimestamp[grantor][requester] + 3 days <= now);
if(!_approve)
lastAudienceDeniedTimestamp[grantor][requester] = block.timestamp;
uint256 amount = getFee(grantor, requester).amount;
clearFee(grantor, requester);
if (!_waive) {
if (_approve) {
require(token.transferFrom(requester, grantor, amount));
}
}
emit AudienceGranted(grantor, requester, _approve);
}
function getGrantAudienceHash(
bytes32 _requesterSignatureHash,
bool _approve,
bool _waive,
bytes32 _secret
)
public
view
returns(bytes32)
{
return keccak256(
abi.encodePacked(
address(this),
bytes4(keccak256("grantAudience(bytes32,bool,bool,bytes32)")),
_requesterSignatureHash,
_approve,
_waive,
_secret
)
);
}
function getRequestAudienceHash(
address _grantor,
bytes32 _hashedSecret,
uint _timeLimit
)
public
view
returns(bytes32)
{
return keccak256(
abi.encodePacked(
address(this),
bytes4(keccak256("requestAudience(address,bytes32,uint256)")),
_grantor,
_hashedSecret,
_timeLimit
)
);
}
/**
* @notice Obtain required fee to talk with `_from`
* @param _from Account `msg.sender` wishes to talk to
* @return Fee
*/
function getFee(address _from, address _to) internal view
returns (Fee memory)
{
Fee memory specificFee = feeCatalog[_from][_to];
Fee memory generalFee = feeCatalog[_from][address(0)];
return specificFee.amount > 0 ? specificFee : generalFee;
}
/**
* @notice Remove any tribute configuration between `_from` and `_to`
* @param _from Owner of the configuration
* @param _to Account that paid tributes (won't after this function is executed)
*/
function clearFee(address _from, address _to) private {
if (!feeCatalog[_from][_to].permanent) {
feeCatalog[_from][_to].amount = 0;
feeCatalog[_from][_to].permanent = false;
}
}
}