This repository has been archived by the owner on Jun 21, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 7
/
Enigma.sol
642 lines (592 loc) · 20.7 KB
/
Enigma.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
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
pragma solidity ^0.5.12;
pragma experimental ABIEncoderV2;
import "openzeppelin-solidity/contracts/math/SafeMath.sol";
import "openzeppelin-solidity/contracts/cryptography/ECDSA.sol";
import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
import "./utils/SolRsaVerify.sol";
import { WorkersImpl } from "./impl/WorkersImpl.sol";
import { PrincipalImpl } from "./impl/PrincipalImpl.sol";
import { TaskImpl } from "./impl/TaskImpl.sol";
import { UpgradeImpl } from "./impl/UpgradeImpl.sol";
import { SecretContractImpl } from "./impl/SecretContractImpl.sol";
import { EnigmaCommon } from "./impl/EnigmaCommon.sol";
import { EnigmaState } from "./impl/EnigmaState.sol";
import { EnigmaEvents } from "./impl/EnigmaEvents.sol";
import { EnigmaStorage } from "./impl/EnigmaStorage.sol";
import { Getters } from "./impl/Getters.sol";
import { ERC20 } from "./interfaces/ERC20.sol";
contract Enigma is EnigmaStorage, EnigmaEvents, Getters, Ownable {
using SafeMath for uint256;
using SafeMath for uint64;
using ECDSA for bytes32;
// ========================================== Constructor ==========================================
constructor(address _tokenAddress, address _principal, address _exchangeRate, uint _epochSize,
uint _timeoutThreshold, bool _debug, bytes memory _mrSigner, bytes memory _isvSvn) public {
state.engToken = ERC20(_tokenAddress);
state.epochSize = _epochSize;
state.taskTimeoutSize = _timeoutThreshold * state.epochSize;
state.principal = _principal;
state.exchangeRate = _exchangeRate;
state.updatedEnigmaContractAddress = address(this);
state.stakingThreshold = 1;
state.workerGroupSize = 1;
state.debug = _debug;
state.mrSigner = _mrSigner;
state.isvSvn = _isvSvn;
}
// ========================================== Modifiers ==========================================
/**
* Checks if the custodian wallet is not registered as a worker
*
* @param _user The custodian address of the worker
*/
modifier workerUnregistered(address _user) {
EnigmaCommon.Worker memory worker = state.workers[_user];
require(worker.status == EnigmaCommon.WorkerStatus.Unregistered, "Registered worker");
_;
}
/**
* Checks if the custodian wallet is registered as a worker
*
* @param _user The custodian address of the worker
*/
modifier workerRegistered(address _user) {
EnigmaCommon.Worker memory worker = state.workers[_user];
require(worker.status != EnigmaCommon.WorkerStatus.Unregistered, "Unregistered worker");
_;
}
/**
* Checks if staking address balance is 0
*
*/
modifier emptyBalance() {
require(state.workers[state.stakingToOperatingAddresses[msg.sender]].balance == 0,
"Worker's balance is not empty");
_;
}
/**
* Checks if the custodian wallet is logged in as a worker
*
*/
modifier workerLoggedIn() {
EnigmaCommon.Worker memory worker = state.workers[msg.sender];
require(worker.status == EnigmaCommon.WorkerStatus.LoggedIn, "Worker not logged in");
_;
}
/**
* Checks if the staking address or operating address is logged in
*
*/
modifier stakingOrOperatingAddressLoggedIn() {
require((state.workers[msg.sender].status == EnigmaCommon.WorkerStatus.LoggedIn) ||
state.workers[state.stakingToOperatingAddresses[msg.sender]].status == EnigmaCommon.WorkerStatus.LoggedIn,
"Worker not logged in");
_;
}
/**
* Checks if the staking address or operating address is registered
*
*/
modifier stakingOrOperatingAddressRegistered() {
require((state.workers[msg.sender].status != EnigmaCommon.WorkerStatus.Unregistered) ||
state.workers[state.stakingToOperatingAddresses[msg.sender]].status != EnigmaCommon.WorkerStatus.Unregistered,
"Unregistered worker");
_;
}
/**
* Checks if worker can log in
*
*/
modifier canLogIn() {
EnigmaCommon.Worker memory worker = state.workers[msg.sender];
require(getFirstBlockNumber(block.number) != 0, "Principal node has not been initialized");
require(worker.status == EnigmaCommon.WorkerStatus.LoggedOut, "Worker not registered or not logged out");
require(worker.balance >= state.stakingThreshold, "Worker's balance is not sufficient");
_;
}
/**
* Checks if the worker can withdraw
*
*/
modifier canWithdraw() {
EnigmaCommon.Worker memory worker = state.workers[state.stakingToOperatingAddresses[msg.sender]];
require(worker.status == EnigmaCommon.WorkerStatus.LoggedOut, "Worker not registered or not logged out");
EnigmaCommon.WorkerLog memory workerLog = WorkersImpl.getLatestWorkerLogImpl(worker, block.number);
require(workerLog.workerEventType == EnigmaCommon.WorkerLogType.LogOut,
"Worker's last log is not of LogOut type");
require(getFirstBlockNumber(block.number) > workerLog.blockNumber,
"Cannot withdraw in same epoch as log out event");
_;
}
/**
* Checks secret contract has not been deployed
*
* @param _scAddr Secret contract address
*/
modifier contractUndefined(bytes32 _scAddr) {
require(state.contracts[_scAddr].status == EnigmaCommon.SecretContractStatus.Undefined,
"Secret contract already deployed");
_;
}
/**
* Checks secret contract has been deployed
*
* @param _scAddr Secret contract address
*/
modifier contractDeployed(bytes32 _scAddr) {
require(state.contracts[_scAddr].status == EnigmaCommon.SecretContractStatus.Deployed,
"Secret contract not deployed");
_;
}
/**
* Checks task record has been created and is still pending receipt
*
* @param _taskId Task ID
*/
modifier taskWaiting(bytes32 _taskId) {
require(state.tasks[_taskId].status == EnigmaCommon.TaskStatus.RecordCreated, "Task is not waiting");
_;
}
/**
* Ensure signing key used for registration is unique
*
*/
modifier areOperatingAndStakingDiff(address _stakingAddress) {
require(_stakingAddress != msg.sender, "Operating address not different from staking address");
_;
}
/**
* Ensure staking address can set an operating address
*
*/
modifier canSetOperatingAddress(address _operatingAddress) {
require(state.stakingToOperatingAddresses[msg.sender] == address(0),
"Staking address currently tied to an in-use operating address");
require(state.workers[_operatingAddress].stakingAddress == msg.sender,
"Invalid staking address for this operating address");
_;
}
/**
* Ensure signing key used for registration is unique
*
* @param _signer Signing key
*/
modifier isUniqueSigningKey(address _signer) {
for (uint i = 0; i < state.workerAddresses.length; i++) {
require(state.workers[state.workerAddresses[i]].signer != _signer,
"Not a unique signing key");
}
_;
}
/**
* Ensure that the contract's context is the updated contract
*
*/
modifier isUpdatedEnigmaContract() {
require(address(this) == state.updatedEnigmaContractAddress, "Not updated Enigma contract");
_;
}
/**
* Ensure the sender of the recent call is the updated contract
*
*/
modifier fromUpdatedEnigmaContract() {
require(msg.sender == state.updatedEnigmaContractAddress, "Not from updated Enigma contract");
_;
}
// ========================================== Functions ==========================================
/**
* Registers a new worker or change the signer parameters of an existing
* worker. This should be called by every worker (and the principal)
* node in order to receive tasks.
*
* @param _stakingAddress The operating address
* @param _signer The signer address, derived from the enclave public key
* @param _report The RLP encoded report returned by the IAS
* @param _signature Signature
*/
function register(address _stakingAddress, address _signer, bytes memory _report, bytes memory _signature)
public
isUpdatedEnigmaContract
workerUnregistered(msg.sender)
isUniqueSigningKey(_signer)
areOperatingAndStakingDiff(_stakingAddress)
{
WorkersImpl.registerImpl(state, _stakingAddress, _signer, _report, _signature);
}
/**
* Unregisters a staking address' worker.
*
*/
function unregister()
public
isUpdatedEnigmaContract
stakingOrOperatingAddressRegistered
emptyBalance
{
WorkersImpl.unregisterImpl(state);
}
/**
* Deposits ENG stake into contract from staking address. Staking address' operating address must be registered.
*
* @param _custodian The staking address to deposit from
* @param _amount The amount of ENG, in grains format (10 ** 8), to deposit
*/
function deposit(address _custodian, uint _amount)
public
isUpdatedEnigmaContract
workerRegistered(state.stakingToOperatingAddresses[_custodian])
{
WorkersImpl.depositImpl(state, _custodian, _amount);
}
/**
* Withdraws ENG stake from contract back to staking address. Staking address' operating address must be registered.
*
* @param _amount The amount of ENG, in grains format (10 ** 8), to withdraw
*/
function withdraw(uint _amount)
public
canWithdraw
{
WorkersImpl.withdrawImpl(state, _amount);
}
/**
* Login worker. Worker must be registered to do so, and must be logged in at start of epoch to be part of worker
* selection process.
*/
function login() public canLogIn {
WorkersImpl.loginImpl(state);
}
/**
* Logout worker. Worker must be logged in to do so.
*/
function logout() public stakingOrOperatingAddressLoggedIn {
WorkersImpl.logoutImpl(state);
}
/**
* Deploy secret contract from user, called by the worker.
*
* @param _taskId Task ID of corresponding deployment task (taskId == scAddr)
* @param _codeHash Deployed bytecode hash
* @param _gasUsed Gas used for task
* @param _sig Worker's signature for deployment
*/
function deploySecretContractFailure(
bytes32 _taskId,
bytes32 _codeHash,
uint64 _gasUsed,
bytes memory _sig
)
public
isUpdatedEnigmaContract
workerLoggedIn
contractUndefined(_taskId)
{
TaskImpl.deploySecretContractFailureImpl(state, _taskId, _codeHash, _gasUsed, _sig);
}
/**
* Deploy secret contract from user, called by the worker.
*
* @param _gasUsed Gas used for task
* @param _optionalEthereumContractAddress Initial state delta hash as a result of the contract's constructor
* @param _bytes32s [taskId, preCodeHash, codeHash, initStateDeltaHash]
* @param _optionalEthereumData Initial state delta hash as a result of the contract's constructor
* @param _sig Worker's signature for deployment
*/
function deploySecretContract(
uint64 _gasUsed,
address _optionalEthereumContractAddress,
bytes32[4] memory _bytes32s,
bytes memory _optionalEthereumData,
bytes memory _sig
)
public
isUpdatedEnigmaContract
workerLoggedIn
contractUndefined(_bytes32s[0])
{
TaskImpl.deploySecretContractImpl(state, _gasUsed, _optionalEthereumContractAddress, _bytes32s,
_optionalEthereumData, _sig);
}
/**
* Check if secret contract has been deployed
*
* @return Number of deployed secret contracts
*/
function countSecretContracts()
public
view
returns (uint)
{
return SecretContractImpl.countSecretContractsImpl(state);
}
/**
* Get deployed secret contract addresses within a range
*
* @param _start Start of range
* @param _stop End of range
* @return Subset of deployed secret contract addresses
*/
function getSecretContractAddresses(uint _start, uint _stop)
public
view
returns (bytes32[] memory)
{
return SecretContractImpl.getSecretContractAddressesImpl(state, _start, _stop);
}
/**
* Create task record for contract deployment. This is necessary for transferring task fee from sender to contract,
* generating the unique taskId, saving the block number when the record was mined, and incrementing the user's
* task deployment counter nonce. We revert the process if the locally-generated nonce value does not match
* the on-chain-computed nonce since this indicates that the secret contract address the user has generated is
* invalid.
*
* @param _inputsHash Hash of encrypted fn sig, encrypted ABI-encoded args, and predeployed bytecode hash
* @param _gasLimit ENG gas limit
* @param _gasPx ENG gas price in grains format (10 ** 8)
* @param _firstBlockNumber Locally-computed first block number of epoch
* @param _nonce Locally-computed nonce value for this deployment
*/
function createDeploymentTaskRecord(
bytes32 _inputsHash,
uint64 _gasLimit,
uint64 _gasPx,
uint _firstBlockNumber,
uint _nonce
)
public
onlyOwner
isUpdatedEnigmaContract
{
TaskImpl.createDeploymentTaskRecordImpl(state, _inputsHash, _gasLimit, _gasPx, _firstBlockNumber, _nonce);
}
/**
* Create task record for task for regular tasks. This is necessary for transferring task fee from sender to
* contract, generating the unique taskId, saving the block number when the record was mined, and incrementing
* the user's task deployment counter nonce.
*
* @param _inputsHash Hash of encrypted fn sig, encrypted ABI-encoded args, and contract address
* @param _gasLimit ENG gas limit
* @param _gasPx ENG gas price in grains format (10 ** 8)
* @param _firstBlockNumber Locally-computed first block number of epoch
*/
function createTaskRecord(
bytes32 _inputsHash,
uint64 _gasLimit,
uint64 _gasPx,
uint _firstBlockNumber
)
public
isUpdatedEnigmaContract
{
TaskImpl.createTaskRecordImpl(state, _inputsHash, _gasLimit, _gasPx, _firstBlockNumber);
}
/**
* Commit the computation task results on chain by first verifying the receipt and then the worker's signature.
* The task record is finalized and the worker is credited with the task fee.
*
* @param _gasUsed Gas used for task computation
* @param _optionalEthereumContractAddress Output state hash
* @param _bytes32s [scAddr, taskId, stateDeltaHash, outputHash]
* @param _optionalEthereumData Output state hash
* @param _sig Worker's signature
*/
function commitReceipt(
uint64 _gasUsed,
address _optionalEthereumContractAddress,
bytes32[4] memory _bytes32s,
bytes memory _optionalEthereumData,
bytes memory _sig
)
public
isUpdatedEnigmaContract
workerLoggedIn
contractDeployed(_bytes32s[0])
{
TaskImpl.commitReceiptImpl(state, _sig, _gasUsed, _optionalEthereumContractAddress,
_bytes32s, _optionalEthereumData);
}
/**
* Commit the computation task failure on chain - the task fee is transfered to the worker and the status is
* updated to indicate task failure.
*
* @param _scAddr Secret contract address
* @param _taskId Unique taskId
* @param _outputHash Output state hash
* @param _gasUsed Gas used for task computation
* @param _sig Worker's signature
*/
function commitTaskFailure(
bytes32 _scAddr,
bytes32 _taskId,
bytes32 _outputHash,
uint64 _gasUsed,
bytes memory _sig
)
public
isUpdatedEnigmaContract
workerLoggedIn
contractDeployed(_scAddr)
{
TaskImpl.commitTaskFailureImpl(state, _scAddr, _taskId, _outputHash, _gasUsed, _sig);
}
/**
* Return the task fee to the task creator when too many blocks have elapsed without task resolution.
*
* @param _taskId Unique taskId
*/
function returnFeesForTask(bytes32 _taskId)
public
taskWaiting(_taskId)
{
TaskImpl.returnFeesForTaskImpl(state, _taskId);
}
/**
* Reparameterizing workers with a new seed
* This should be called for each epoch by the Principal node
*
* @param _blockNumber Block number principal node is attempting to set worker params
* @param _seed The random integer generated by the enclave
* @param _sig Principal node's signature
*/
function setWorkersParams(uint _blockNumber, uint _seed, bytes memory _sig)
public
isUpdatedEnigmaContract
workerRegistered(msg.sender)
{
PrincipalImpl.setWorkersParamsImpl(state, _blockNumber, _seed, _sig);
}
/**
* Get active workers before a certain block number
*
* @param _blockNumber Block number
*/
function getActiveWorkers(uint _blockNumber)
public
view
returns (address[] memory, uint[] memory)
{
return PrincipalImpl.getActiveWorkersImpl(state, _blockNumber);
}
/**
* Get the first block number of an epoch that a given block number belongs to
*
* @param _blockNumber Block number
* @return Block number
*/
function getFirstBlockNumber(uint _blockNumber)
public
view
returns (uint) {
return WorkersImpl.getFirstBlockNumberImpl(state, _blockNumber);
}
/**
* Get worker params for an epoch given a particular block number
*
* @param _blockNumber Block number
* @return Epoch's first block number
* @return Seed
* @return Array of worker's signing addresses
* @return Array of worker's stakes
*/
function getWorkerParams(uint _blockNumber)
public
view
returns (uint, uint, address[] memory, uint[] memory) {
return WorkersImpl.getWorkerParamsImpl(state, _blockNumber);
}
/**
* Select a group of workers for the computation task given the block number of the task record (implies the epoch)
* and the secret contract address.
*
* @param _blockNumber Block number the task record was mined
* @param _scAddr Secret contract address
* @return Selected workers' addresses
*/
function getWorkerGroup(uint _blockNumber, bytes32 _scAddr)
public
view
returns (address[] memory)
{
return WorkersImpl.getWorkerGroupImpl(state, _blockNumber, _scAddr);
}
/**
* The RLP encoded report returned by the IAS server
*
* @param _custodian The worker's custodian address
*/
function getReport(address _custodian)
public
view
workerRegistered(_custodian)
returns (address, bytes memory)
{
return WorkersImpl.getReportImpl(state, _custodian);
}
/**
* This verifies an IAS report with hard coded modulus and exponent of Intel's certificate.
* @param _data The report itself
* @param _signature The signature of the report
*/
function verifyReport(bytes memory _data, bytes memory _signature)
public
view
returns (uint)
{
return WorkersImpl.verifyReportImpl(_data, _signature);
}
/**
* Upgrade Enigma Contract
* @param _updatedEnigmaContractAddress Updated newly-deployed Enigma contract address
*/
function upgradeEnigmaContract(address _updatedEnigmaContractAddress)
public
onlyOwner
isUpdatedEnigmaContract
{
return UpgradeImpl.upgradeEnigmaContractImpl(state, _updatedEnigmaContractAddress);
}
/**
* Transfer worker stake from old contract to new contract upon registration
* @param _operatingAddress Operating Address
* @param _stakingAddress Newly-registered worker address
* @param _sig Signature
*/
function transferWorkerStakePostUpgrade(address _operatingAddress, address _stakingAddress, bytes memory _sig)
public
fromUpdatedEnigmaContract
returns (uint256)
{
return UpgradeImpl.transferWorkerStakePostUpgradeImpl(state, _operatingAddress, _stakingAddress, _sig);
}
/**
* Set mrSigner
* @param _mrSigner mrSigner
*/
function setMrSigner(bytes memory _mrSigner)
public
onlyOwner
{
state.mrSigner = _mrSigner;
}
/**
* Set isvSvn
* @param _isvSvn mrSigner
*/
function setIsvSvn(bytes memory _isvSvn)
public
onlyOwner
{
state.isvSvn = _isvSvn;
}
/**
* Set operating address for a particular staking address
* @param _operatingAddress Operating Address
*/
function setOperatingAddress(address _operatingAddress)
public
canSetOperatingAddress(_operatingAddress)
{
WorkersImpl.setOperatingAddressImpl(state, _operatingAddress);
}
}