forked from hyperledger/cacti
/
PrivateHashTimeLock.sol
176 lines (139 loc) · 5.13 KB
/
PrivateHashTimeLock.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
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
contract PrivateHashTimeLock {
mapping(bytes32 => LockContract) private contracts;
// / - WITHDRAWN
// INVALID - ACTIVE |
// \ - EXPIRED - REFUNDED
uint256 private constant INIT = 0; // INIT swap -> can go to ACTIVE
uint256 private constant ACTIVE = 1; // Active swap -> can go to WITHDRAWN or EXPIRED
uint256 private constant REFUNDED = 2; // Swap is refunded -> final state.
uint256 private constant WITHDRAWN = 3; // Swap is withdrawn -> final state.
uint256 private constant EXPIRED = 4; // Swap is expired -> can go to REFUNDED
struct PrivateEnhancing {
// should be large
uint256 generator;
// should be large
uint256 modulus;
}
struct SwapDetails {
address payable sender;
address payable receiver;
uint256 inputAmount;
bytes32 hashSecret;
uint256 expiration;
}
struct LockContract {
uint256 inputAmount;
uint256 outputAmount;
uint256 expiration;
uint256 status;
bytes32 hashSecret;
address payable sender;
address payable receiver;
string outputNetwork;
string outputAddress;
PrivateEnhancing priv;
}
event Withdraw(
bytes32 indexed id, bytes32 secret, bytes32 hashSecret, address indexed sender, address indexed receiver
);
event Refund(bytes32 indexed id, bytes32 hashSecret, address indexed sender, address indexed receiver);
event NewContract(
uint256 inputAmount,
uint256 outputAmount,
uint256 expiration,
bytes32 indexed id,
bytes32 hashSecret,
address indexed sender,
address indexed receiver,
string outputNetwork,
string outputAddress,
PrivateEnhancing priv
);
function newPrivateContract(
uint256 outputAmount,
uint256 expiration,
bytes32 hashLock,
address payable receiver,
string memory outputNetwork,
string memory outputAddress,
PrivateEnhancing memory priv
) external payable {
address payable sender = payable(msg.sender);
uint256 inputAmount = msg.value;
require(expiration > block.timestamp, "INVALID_TIME");
require(inputAmount > 0, "INVALID_AMOUNT");
bytes32 id = keccak256(abi.encodePacked(sender, receiver, inputAmount, hashLock, expiration));
require(contracts[id].status == INIT, "SWAP_EXISTS");
require(priv.generator > 0);
require(priv.modulus > 0);
contracts[id] = LockContract(
inputAmount,
outputAmount,
expiration,
ACTIVE,
hashLock,
sender,
receiver,
outputNetwork,
outputAddress,
priv
);
emit NewContract(
inputAmount, outputAmount, expiration, id, hashLock, sender, receiver, outputNetwork, outputAddress, priv
);
}
function withdraw(bytes32 id, bytes32 secret) external {
LockContract storage c = contracts[id];
require(c.status == ACTIVE, "SWAP_NOT_ACTIVE");
require(c.expiration > block.timestamp, "INVALID_TIME");
require(
c.hashSecret == calculateHashSecret(c.priv.generator, uint256(secret), c.priv.modulus), "INVALID_SECRET"
);
c.status = WITHDRAWN;
c.receiver.transfer(c.inputAmount);
emit Withdraw(id, secret, c.hashSecret, c.sender, c.receiver);
}
function calculateHashSecret(uint256 base, uint256 exponent, uint256 modulus)
internal
pure
returns (bytes32 result)
{
require(modulus > 0, "Modulus cannot be 0");
require(base > 0, "base cannot be 0");
require(exponent > 0, "exponent_1 cannot be 0");
return bytes32((base ** exponent) % modulus);
}
function refund(bytes32 id) external {
LockContract storage c = contracts[id];
require(c.status == ACTIVE, "SWAP_NOT_ACTIVE");
require(c.expiration <= block.timestamp, "INVALID_TIME");
c.status = REFUNDED;
c.sender.transfer(c.inputAmount);
emit Refund(id, c.hashSecret, c.sender, c.receiver);
}
function getStatus(bytes32[] memory ids) public view returns (uint256[] memory) {
uint256[] memory result = new uint256[](ids.length);
for (uint256 index = 0; index < ids.length; index++) {
result[index] = getSingleStatus(ids[index]);
}
return result;
}
function getSingleStatus(bytes32 id) public view returns (uint256 result) {
LockContract memory tempContract = contracts[id];
if (tempContract.status == ACTIVE && tempContract.expiration < block.timestamp) {
result = EXPIRED;
} else {
result = tempContract.status;
}
}
function contractExists(bytes32 id) public view returns (bool result) {
LockContract memory tempContract = contracts[id];
if (tempContract.status == INIT) {
return false;
} else {
return true;
}
}
}