@@ -9,6 +9,7 @@ import { PPMMath } from "../../libraries/PPMMath.sol";
9
9
10
10
import { GraphDirectory } from "../../utilities/GraphDirectory.sol " ;
11
11
import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol " ;
12
+ import { MessageHashUtils } from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol " ;
12
13
13
14
/**
14
15
* @title TAPCollector contract
@@ -29,21 +30,89 @@ contract TAPCollector is EIP712, GraphDirectory, ITAPCollector {
29
30
"ReceiptAggregateVoucher(address dataService,address serviceProvider,uint64 timestampNs,uint128 valueAggregate,bytes metadata) "
30
31
);
31
32
33
+ /// @notice Authorization details for payer-signer pairs
34
+ mapping (address signer = > PayerAuthorization authorizedSigner ) public authorizedSigners;
35
+
32
36
/// @notice Tracks the amount of tokens already collected by a data service from a payer to a receiver
33
37
mapping (address dataService = > mapping (address receiver = > mapping (address payer = > uint256 tokens )))
34
38
public tokensCollected;
35
39
40
+ /// @notice The duration (in seconds) in which a signer is thawing before they can be revoked
41
+ uint256 public immutable REVOKE_SIGNER_THAWING_PERIOD;
42
+
36
43
/**
37
44
* @notice Constructs a new instance of the TAPVerifier contract.
38
45
* @param eip712Name The name of the EIP712 domain.
39
46
* @param eip712Version The version of the EIP712 domain.
40
47
* @param controller The address of the Graph controller.
48
+ * @param revokeSignerThawingPeriod The duration (in seconds) in which a signer is thawing before they can be revoked.
41
49
*/
42
50
constructor (
43
51
string memory eip712Name ,
44
52
string memory eip712Version ,
45
- address controller
46
- ) EIP712 (eip712Name, eip712Version) GraphDirectory (controller) {}
53
+ address controller ,
54
+ uint256 revokeSignerThawingPeriod
55
+ ) EIP712 (eip712Name, eip712Version) GraphDirectory (controller) {
56
+ REVOKE_SIGNER_THAWING_PERIOD = revokeSignerThawingPeriod;
57
+ }
58
+
59
+ /**
60
+ * See {ITAPCollector.authorizeSigner}.
61
+ */
62
+ function authorizeSigner (address signer , uint256 proofDeadline , bytes calldata proof ) external override {
63
+ require (
64
+ authorizedSigners[signer].payer == address (0 ),
65
+ TAPCollectorSignerAlreadyAuthorized (authorizedSigners[signer].payer, signer)
66
+ );
67
+
68
+ _verifyAuthorizedSignerProof (proof, proofDeadline, signer);
69
+
70
+ authorizedSigners[signer].payer = msg .sender ;
71
+ authorizedSigners[signer].thawEndTimestamp = 0 ;
72
+ emit SignerAuthorized (msg .sender , signer);
73
+ }
74
+
75
+ /**
76
+ * See {ITAPCollector.thawSigner}.
77
+ */
78
+ function thawSigner (address signer ) external override {
79
+ PayerAuthorization storage authorization = authorizedSigners[signer];
80
+
81
+ require (authorization.payer == msg .sender , TAPCollectorSignerNotAuthorizedByPayer (msg .sender , signer));
82
+
83
+ authorization.thawEndTimestamp = block .timestamp + REVOKE_SIGNER_THAWING_PERIOD;
84
+ emit SignerThawing (msg .sender , signer, authorization.thawEndTimestamp);
85
+ }
86
+
87
+ /**
88
+ * See {ITAPCollector.cancelThawSigner}.
89
+ */
90
+ function cancelThawSigner (address signer ) external override {
91
+ PayerAuthorization storage authorization = authorizedSigners[signer];
92
+
93
+ require (authorization.payer == msg .sender , TAPCollectorSignerNotAuthorizedByPayer (msg .sender , signer));
94
+ require (authorization.thawEndTimestamp > 0 , TAPCollectorSignerNotThawing (signer));
95
+
96
+ authorization.thawEndTimestamp = 0 ;
97
+ emit SignerThawCanceled (msg .sender , signer, 0 );
98
+ }
99
+
100
+ /**
101
+ * See {ITAPCollector.revokeAuthorizedSigner}.
102
+ */
103
+ function revokeAuthorizedSigner (address signer ) external override {
104
+ PayerAuthorization storage authorization = authorizedSigners[signer];
105
+
106
+ require (authorization.payer == msg .sender , TAPCollectorSignerNotAuthorizedByPayer (msg .sender , signer));
107
+ require (authorization.thawEndTimestamp > 0 , TAPCollectorSignerNotThawing (signer));
108
+ require (
109
+ authorization.thawEndTimestamp <= block .timestamp ,
110
+ TAPCollectorSignerStillThawing (block .timestamp , authorization.thawEndTimestamp)
111
+ );
112
+
113
+ delete authorizedSigners[signer];
114
+ emit SignerRevoked (msg .sender , signer);
115
+ }
47
116
48
117
/**
49
118
* @notice Initiate a payment collection through the payments protocol
@@ -58,59 +127,73 @@ contract TAPCollector is EIP712, GraphDirectory, ITAPCollector {
58
127
TAPCollectorCallerNotDataService (msg .sender , signedRAV.rav.dataService)
59
128
);
60
129
61
- address dataService = signedRAV.rav.dataService;
62
- address payer = _recoverRAVSigner (signedRAV);
63
- address receiver = signedRAV.rav.serviceProvider;
130
+ address signer = _recoverRAVSigner (signedRAV);
131
+ require (authorizedSigners[signer].payer != address (0 ), TAPCollectorInvalidRAVSigner ());
132
+
133
+ return _collect (paymentType, authorizedSigners[signer].payer, signedRAV, dataServiceCut);
134
+ }
135
+
136
+ /**
137
+ * @notice See {ITAPCollector.recoverRAVSigner}
138
+ */
139
+ function recoverRAVSigner (SignedRAV calldata signedRAV ) external view override returns (address ) {
140
+ return _recoverRAVSigner (signedRAV);
141
+ }
142
+
143
+ /**
144
+ * @notice See {ITAPCollector.encodeRAV}
145
+ */
146
+ function encodeRAV (ReceiptAggregateVoucher calldata rav ) external view returns (bytes32 ) {
147
+ return _encodeRAV (rav);
148
+ }
64
149
65
- uint256 tokensRAV = signedRAV.rav.valueAggregate;
66
- uint256 tokensAlreadyCollected = tokensCollected[dataService][receiver][payer];
150
+ /**
151
+ * @notice See {ITAPCollector.collect}
152
+ */
153
+ function _collect (
154
+ IGraphPayments.PaymentTypes _paymentType ,
155
+ address _payer ,
156
+ SignedRAV memory _signedRAV ,
157
+ uint256 _dataServiceCut
158
+ ) private returns (uint256 ) {
159
+ address dataService = _signedRAV.rav.dataService;
160
+ address receiver = _signedRAV.rav.serviceProvider;
161
+
162
+ uint256 tokensRAV = _signedRAV.rav.valueAggregate;
163
+ uint256 tokensAlreadyCollected = tokensCollected[dataService][receiver][_payer];
67
164
require (
68
165
tokensRAV > tokensAlreadyCollected,
69
166
TAPCollectorInconsistentRAVTokens (tokensRAV, tokensAlreadyCollected)
70
167
);
71
168
72
169
uint256 tokensToCollect = tokensRAV - tokensAlreadyCollected;
73
- uint256 tokensDataService = tokensToCollect.mulPPM (dataServiceCut );
170
+ uint256 tokensDataService = tokensToCollect.mulPPM (_dataServiceCut );
74
171
75
172
if (tokensToCollect > 0 ) {
173
+ tokensCollected[dataService][receiver][_payer] = tokensRAV;
76
174
_graphPaymentsEscrow ().collect (
77
- paymentType ,
78
- payer ,
175
+ _paymentType ,
176
+ _payer ,
79
177
receiver,
80
178
tokensToCollect,
81
179
dataService,
82
180
tokensDataService
83
181
);
84
- tokensCollected[dataService][receiver][payer] = tokensRAV;
85
182
}
86
183
87
- emit PaymentCollected (paymentType, payer , receiver, tokensToCollect, dataService, tokensDataService);
184
+ emit PaymentCollected (_paymentType, _payer , receiver, tokensToCollect, dataService, tokensDataService);
88
185
emit RAVCollected (
89
- payer ,
186
+ _payer ,
90
187
dataService,
91
188
receiver,
92
- signedRAV .rav.timestampNs,
93
- signedRAV .rav.valueAggregate,
94
- signedRAV .rav.metadata,
95
- signedRAV .signature
189
+ _signedRAV .rav.timestampNs,
190
+ _signedRAV .rav.valueAggregate,
191
+ _signedRAV .rav.metadata,
192
+ _signedRAV .signature
96
193
);
97
194
return tokensToCollect;
98
195
}
99
196
100
- /**
101
- * @notice See {ITAPCollector.recoverRAVSigner}
102
- */
103
- function recoverRAVSigner (SignedRAV calldata signedRAV ) external view override returns (address ) {
104
- return _recoverRAVSigner (signedRAV);
105
- }
106
-
107
- /**
108
- * @notice See {ITAPCollector.encodeRAV}
109
- */
110
- function encodeRAV (ReceiptAggregateVoucher calldata rav ) external view returns (bytes32 ) {
111
- return _encodeRAV (rav);
112
- }
113
-
114
197
/**
115
198
* @notice See {ITAPCollector.recoverRAVSigner}
116
199
*/
@@ -137,4 +220,27 @@ contract TAPCollector is EIP712, GraphDirectory, ITAPCollector {
137
220
)
138
221
);
139
222
}
223
+
224
+ /**
225
+ * @notice Verify the proof provided by the payer authorizing the signer
226
+ * @param _proof The proof provided by the payer authorizing the signer
227
+ * @param _proofDeadline The deadline by which the proof must be verified
228
+ * @param _signer The signer to be authorized
229
+ */
230
+ function _verifyAuthorizedSignerProof (bytes calldata _proof , uint256 _proofDeadline , address _signer ) private view {
231
+ // Verify that the proofDeadline has not passed
232
+ require (
233
+ _proofDeadline > block .timestamp ,
234
+ TAPCollectorInvalidSignerProofDeadline (_proofDeadline, block .timestamp )
235
+ );
236
+
237
+ // Generate the hash of the payer's address
238
+ bytes32 messageHash = keccak256 (abi.encodePacked (block .chainid , _proofDeadline, msg .sender ));
239
+
240
+ // Generate the digest to be signed by the signer
241
+ bytes32 digest = MessageHashUtils.toEthSignedMessageHash (messageHash);
242
+
243
+ // Verify that the recovered signer matches the expected signer
244
+ require (ECDSA.recover (digest, _proof) == _signer, TAPCollectorInvalidSignerProof ());
245
+ }
140
246
}
0 commit comments