/
VIDT commented OLD.sol
446 lines (316 loc) · 14.6 KB
/
VIDT commented OLD.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
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
pragma solidity ^0.4.24;
/*
This solidity version supports auto-generated getter functions for all public state variables.
The default ERC20 getter functions for name(), symbol(), decimals() and totalSupply() are created by the compiler.
*/
contract ERC20 {
/*
Full ERC20 EIP20 compliancy
Credits https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
*/
uint256 public totalSupply;
function balanceOf(address who) public view returns (uint256 balance);
function allowance(address owner, address spender) public view returns (uint256 remaining);
function transfer(address to, uint256 value) public returns (bool success);
function approve(address spender, uint256 value) public returns (bool success);
function transferFrom(address from, address to, uint256 value) public returns (bool success);
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}
library SafeMath {
/*
SafeMath library with small extension to the assert statements, adds or subtracts two numbers, reverts on overflow
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256 c) {
c = a - b;
assert(b <= a && c <= a);
return c;
}
function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
c = a + b;
assert(c >= a && c>=b);
return c;
}
}
library SafeERC20 {
/*
SafeERC20 library just here to transfer any non V-ID tokens accidentally sent to the contract, can be used to reimburse senders
Credits https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/token/ERC20/SafeERC20.sol (not identical, tailored to V-ID)
*/
function safeTransfer(ERC20 _token, address _to, uint256 _value) internal {
require(_token.transfer(_to, _value));
}
}
contract Owned {
/*
The contract is Owned so ownership can be transferred and some functions can only be invoked by the owner
Credits https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/ownership/Ownable.sol (not identical, tailored to V-ID)
*/
address public owner;
constructor() public {
owner = msg.sender;
}
modifier onlyOwner {
require(msg.sender == owner,"O1- Owner only function");
_;
}
function setOwner(address newOwner) onlyOwner public {
owner = newOwner;
}
}
contract Pausable is Owned {
/*
The contract is Pauseable so token transfers can be paused in case of a security risk or Ethereum protocol problem
Credits https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/token/ERC20/PausableToken.sol (not identical, tailored to V-ID)
*/
event Pause();
event Unpause();
bool public paused = false;
modifier whenNotPaused() {
require(!paused);
_;
}
modifier whenPaused() {
require(paused);
_;
}
function pause() public onlyOwner whenNotPaused {
paused = true;
emit Pause();
}
function unpause() public onlyOwner whenPaused {
paused = false;
emit Unpause();
}
}
contract VIDToken is Owned, Pausable, ERC20 {
/*
The V-ID contract itself, using all the before mentioned defaults and libraries
*/
// Applying the SafeMath library to all integers used
using SafeMath for uint256;
// Applying the SafeERC20 library to all ERC20 instances
using SafeERC20 for ERC20;
// Default ERC20 array for storing the balances
mapping (address => uint256) public balances;
// Default ERC20 array for storing the allowances between accounts
mapping (address => mapping (address => uint256)) public allowed;
// A custom array to store accounts that are (temporarily) frozen, to be able to prevent abuse
mapping (address => bool) public frozenAccount;
// A custom array to store all verified publisher accounts
mapping (address => bool) public verifyPublisher;
// A custom array to store all verified validation wallets
mapping (address => bool) public verifyWallet;
// Simple structure for the storage of the hashes
struct fStruct { uint256 index; }
// The hashes are stored in this array
mapping(string => fStruct) private fileHashes;
// The array of hashes is indexed in this array for queries
string[] private fileIndex;
// The name of the token
string public constant name = "V-ID Token";
// The number of decimals, 18 for most broadly used implementation
uint8 public constant decimals = 18;
// The preferred ticker / symbol of the token
string public constant symbol = "VIDT";
// The initial supply (lacking the 18 decimals) 100.000.000 VIDT
uint256 public constant initialSupply = 100000000;
// The default validation price per hash
uint256 public validationPrice = 7 * 10 ** uint(decimals);
// The default validation wallet
address public validationWallet = address(0);
// The constructor is invoked on contract creation, it sets the contract owner and default wallet
constructor() public {
validationWallet = msg.sender;
verifyWallet[msg.sender] = true;
totalSupply = initialSupply * 10 ** uint(decimals);
balances[msg.sender] = totalSupply;
emit Transfer(address(0),owner,initialSupply);
}
// The fallback function which will be invoked when the function signature is not found, the revert() prevents the contract from receiving Ethereum
function () public payable {
revert();
}
// The default ERC20 transfer() function is extended to be pausable and to prevent transfers from or to frozen accounts
function transfer(address _to, uint256 _value) public whenNotPaused returns (bool success) {
require(_to != msg.sender,"T1- Recipient can not be the same as sender");
require(_to != address(0),"T2- Please check the recipient address");
require(balances[msg.sender] >= _value,"T3- The balance of sender is too low");
require(!frozenAccount[msg.sender],"T4- The wallet of sender is frozen");
require(!frozenAccount[_to],"T5- The wallet of recipient is frozen");
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(_value);
emit Transfer(msg.sender, _to, _value);
return true;
}
// The default ERC20 transferFrom() function is extended to be pausable and to prevent transfers from or to frozen accounts
function transferFrom(address _from, address _to, uint256 _value) public whenNotPaused returns (bool success) {
require(_to != address(0),"TF1- Please check the recipient address");
require(balances[_from] >= _value,"TF2- The balance of sender is too low");
require(allowed[_from][msg.sender] >= _value,"TF3- The allowance of sender is too low");
require(!frozenAccount[_from],"TF4- The wallet of sender is frozen");
require(!frozenAccount[_to],"TF5- The wallet of recipient is frozen");
balances[_from] = balances[_from].sub(_value);
balances[_to] = balances[_to].add(_value);
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
emit Transfer(_from, _to, _value);
return true;
}
// Default ERC20 balanceOf() returns the current balance of _owner
function balanceOf(address _owner) public view returns (uint256 balance) {
return balances[_owner];
}
// Default ERC20 approve() extended to prevent abuse under race conditions by requiring a 0 allowance before approving
function approve(address _spender, uint256 _value) public whenNotPaused returns (bool success) {
require((_value == 0) || (allowed[msg.sender][_spender] == 0),"A1- Reset allowance to 0 first");
allowed[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
// The increaseApproval() function is here to efficiently increase any allowance in 1 transaction, protected against overflow bug using SafeMath
function increaseApproval(address _spender, uint256 _addedValue) public whenNotPaused returns (bool) {
allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue);
emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}
// The decreaseApproval() function is here to efficiently decrease any allowance in 1 transaction, protected against overflow bug using SafeMath
function decreaseApproval(address _spender, uint256 _subtractedValue) public whenNotPaused returns (bool) {
allowed[msg.sender][_spender] = allowed[msg.sender][_spender].sub(_subtractedValue);
emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}
// Default ERC20 allowance() returns the current allowance of _spender using tokens of _owner
function allowance(address _owner, address _spender) public view returns (uint256 remaining) {
return allowed[_owner][_spender];
}
// The struct is a named array to be used in the tokenFallback() function
struct TKN { address sender; uint256 value; bytes data; bytes4 sig; }
// Though the tokenFallback() is a pure bogey, it's existence confirms the contract's ability to transfer any token (ERC223 compatibility)
function tokenFallback(address _from, uint256 _value, bytes _data) public pure returns (bool) {
TKN memory tkn;
tkn.sender = _from;
tkn.value = _value;
tkn.data = _data;
uint32 u = uint32(_data[3]) + (uint32(_data[2]) << 8) + (uint32(_data[1]) << 16) + (uint32(_data[0]) << 24);
tkn.sig = bytes4(u);
return true;
}
// The transferToken() is the function that is actually used by the contract owner to transfer tokens to the owner wallet so these can be reimbursed to any sender
function transferToken(address tokenAddress, uint256 tokens) public onlyOwner {
ERC20(tokenAddress).safeTransfer(owner,tokens);
}
// The V-ID tokenomics model is set to burn() a minimum of 10% of all tokens received in payment for validations and implementations, tokens are burned by sending them to the (0) address
function burn(uint256 _value) public onlyOwner returns (bool) {
require(_value <= balances[msg.sender],"B1- The balance of burner is too low");
balances[msg.sender] = balances[msg.sender].sub(_value);
totalSupply = totalSupply.sub(_value);
emit Burn(msg.sender, _value);
emit Transfer(msg.sender, address(0), _value);
return true;
}
// The freeze() function is used to prevent abuse or protecting funds by freezing accounts
function freeze(address _address, bool _state) public onlyOwner returns (bool) {
frozenAccount[_address] = _state;
emit Freeze(_address, _state);
return true;
}
// All publishers are identified and validated by wallet / account and are named in the validation transaction
function validatePublisher(address Address, bool State, string Publisher) public onlyOwner returns (bool) {
verifyPublisher[Address] = State;
emit ValidatePublisher(Address,State,Publisher);
return true;
}
// Multiple wallets can be used to receive the V-ID Token payments, the contract owner / admin can (in)validate wallets using this function
function validateWallet(address Address, bool State, string Wallet) public onlyOwner returns (bool) {
verifyWallet[Address] = State;
emit ValidateWallet(Address,State,Wallet);
return true;
}
/*
The essence of the validation process is the validation of a files fingerprint / hash using the validateFile() function.
The hash is alway stored in the transaction log (input) and optionally (though default) in the contract itself and in the event logs.
In this way the validateFile() function provides the use of all 3 tiers in the data storage logic of the Ethereum blockchain (transaction, state and receipt).
Herein also lies the logic of using the token as method of most efficient payment for each micro transaction.
As we will be using automated validation tools (also API's) it is easy to vary the price per validation, therefore the validationPrice variable only represents the minimum amount.
*/
function validateFile(address To, uint256 Payment, bytes Data, bool cStore, bool eLog) public whenNotPaused returns (bool) {
require(Payment>=validationPrice,"V1- Insufficient payment provided");
require(verifyPublisher[msg.sender],"V2- Unverified publisher address");
require(!frozenAccount[msg.sender],"V3- The wallet of publisher is frozen");
require(Data.length == 64,"V4- Invalid hash provided");
if (!verifyWallet[To] || frozenAccount[To]) {
To = validationWallet;
}
uint256 index = 0;
string memory fileHash = string(Data);
if (cStore) {
if (fileIndex.length > 0) {
require(fileHashes[fileHash].index == 0,"V5- This hash was previously validated");
}
fileHashes[fileHash].index = fileIndex.push(fileHash)-1;
index = fileHashes[fileHash].index;
}
if (allowed[To][msg.sender] >= Payment) {
allowed[To][msg.sender] = allowed[To][msg.sender].sub(Payment);
} else {
balances[msg.sender] = balances[msg.sender].sub(Payment);
balances[To] = balances[To].add(Payment);
}
emit Transfer(msg.sender, To, Payment);
if (eLog) {
emit ValidateFile(index,fileHash);
}
return true;
}
/*
Verifying a file is available at zero cost as it does not require a transaction, just a query.
The verifyFile() function matches twice, first by retrieving the index of a file by its hash (string) and again by comparing the strings 1 character at a time.
*/
function verifyFile(string fileHash) public view returns (bool) {
if (fileIndex.length == 0) {
return false;
}
bytes memory a = bytes(fileIndex[fileHashes[fileHash].index]);
bytes memory b = bytes(fileHash);
if (a.length != b.length) {
return false;
}
for (uint256 i = 0; i < a.length; i ++) {
if (a[i] != b[i]) {
return false;
}
}
return true;
}
// The contract owner / V-ID can set the price for each validation using the setPrice() function
function setPrice(uint256 newPrice) public onlyOwner {
validationPrice = newPrice;
}
// The contract owner / V-ID can set the default wallet for payments using the setWallet() function
function setWallet(address newWallet) public onlyOwner {
validationWallet = newWallet;
}
// The listFiles() function is to provide direct access to the data for audits and consistency checks
function listFiles(uint256 startAt, uint256 stopAt) onlyOwner public returns (bool) {
if (fileIndex.length == 0) {
return false;
}
require(startAt <= fileIndex.length-1,"L1- Please select a valid start");
if (stopAt > 0) {
require(stopAt > startAt && stopAt <= fileIndex.length-1,"L2- Please select a valid stop");
} else {
stopAt = fileIndex.length-1;
}
for (uint256 i = startAt; i <= stopAt; i++) {
emit LogEvent(i,fileIndex[i]);
}
return true;
}
// All possible custom events are listed here for their signatures
event Burn(address indexed burner, uint256 value);
event Freeze(address target, bool frozen);
event ValidateFile(uint256 index, string data);
event ValidatePublisher(address indexed publisherAddress, bool state, string indexed publisherName);
event ValidateWallet(address indexed walletAddress, bool state, string indexed walletName);
event LogEvent(uint256 index, string data) anonymous;
}