Skip to content

Commit d0a4db2

Browse files
f: upgrade WIP
1 parent cc2775b commit d0a4db2

File tree

5 files changed

+155
-35
lines changed

5 files changed

+155
-35
lines changed

Diff for: IndexingPaymentsTodo.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
* Update Arbitration Charter to support disputing Indexing Fees. DONE: ~~Support `DisputeManager`~~
55
* Make `agreementId` unique globally so that we don't need the full tuple (`payer`+`indexer`+`agreementId`) as key?
66
* Rename `Decoder.sol` - Maybe turn it into a library instead?
7-
* Support indexing agreement upgadeability, so that there is a mechanism to adjust the rates without having to cancel and start over.
87
* Economics
98
* If service wants to collect more than collector allows. Collector limits but doesn't tell the service?
109
* Support for agreements that end up in `RecurringCollectorCollectionTooLate` or ways to avoid getting to that state.
@@ -19,3 +18,4 @@
1918
* Expose a function that indexers can use to calculate the tokens to be collected and other collection params?
2019
* Support a way for gateway to shop an agreement around? Deadline + dedup key? So only one agreement with the dedupe key can be accepted?
2120
* Maybe check that the epoch the indexer is sending is the one the transaction will be run in?
21+
* Check upgrade conditions. DONE: ~~Support indexing agreement upgadeability, so that there is a mechanism to adjust the rates without having to cancel and start over~~.

Diff for: packages/horizon/contracts/interfaces/IRecurringCollector.sol

+9
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ interface IRecurringCollector is IAuthorizable, IPaymentsCollector {
6767
uint32 minSecondsPerCollection;
6868
// The maximum amount of seconds that can pass between collections
6969
uint32 maxSecondsPerCollection;
70+
// The agreement ID of the previous agreement
71+
bytes16 updatedFromAgreementId;
7072
}
7173

7274
/// @notice The key for a stored agreement
@@ -202,6 +204,13 @@ interface IRecurringCollector is IAuthorizable, IPaymentsCollector {
202204
*/
203205
function cancel(address payer, address serviceProvider, bytes16 agreementId) external;
204206

207+
/**
208+
* @dev Upgrade an indexing agreement.
209+
* @param oldAgreementId The agreement ID of the old agreement.
210+
* @param signedRCV The signed Recurrent Collection Voucher of the new agreement.
211+
*/
212+
function upgrade(bytes16 oldAgreementId, SignedRCV memory signedRCV) external;
213+
205214
/**
206215
* @dev Computes the hash of a RecurrentCollectionVoucher (RCV).
207216
* @param rcv The RCV for which to compute the hash.

Diff for: packages/horizon/contracts/payments/collectors/RecurringCollector.sol

+84-33
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ contract RecurringCollector is EIP712, GraphDirectory, Authorizable, IRecurringC
3636
* @param dataService The address of the dataService
3737
*/
3838
modifier onlyDataService(address dataService) {
39-
require(dataService == msg.sender, RecurringCollectorCallerNotDataService(msg.sender, dataService));
39+
require(msg.sender == dataService, RecurringCollectorCallerNotDataService(msg.sender, dataService));
4040
_;
4141
}
4242

@@ -77,32 +77,7 @@ contract RecurringCollector is EIP712, GraphDirectory, Authorizable, IRecurringC
7777
* @dev Caller must be the data service the RCV was issued to.
7878
*/
7979
function accept(SignedRCV memory signedRCV) external onlyDataService(signedRCV.rcv.dataService) {
80-
require(
81-
signedRCV.rcv.acceptDeadline >= block.timestamp,
82-
RecurringCollectorAgreementAcceptanceElapsed(signedRCV.rcv.acceptDeadline)
83-
);
84-
85-
// check that the voucher is signed by the payer (or proxy)
86-
_requireAuthorizedRCVSigner(signedRCV);
87-
88-
AgreementKey memory key = AgreementKey({
89-
dataService: signedRCV.rcv.dataService,
90-
payer: signedRCV.rcv.payer,
91-
serviceProvider: signedRCV.rcv.serviceProvider,
92-
agreementId: signedRCV.rcv.agreementId
93-
});
94-
AgreementData storage agreement = _getForUpdateAgreement(key);
95-
// check that the agreement is not already accepted
96-
require(agreement.acceptedAt == 0, RecurringCollectorAgreementAlreadyAccepted(key));
97-
98-
// accept the agreement
99-
agreement.acceptedAt = block.timestamp;
100-
// FIX-ME: These need to be validated to something that makes sense for the contract
101-
agreement.duration = signedRCV.rcv.duration;
102-
agreement.maxInitialTokens = signedRCV.rcv.maxInitialTokens;
103-
agreement.maxOngoingTokensPerSecond = signedRCV.rcv.maxOngoingTokensPerSecond;
104-
agreement.minSecondsPerCollection = signedRCV.rcv.minSecondsPerCollection;
105-
agreement.maxSecondsPerCollection = signedRCV.rcv.maxSecondsPerCollection;
80+
_accept(signedRCV);
10681
}
10782

10883
/**
@@ -111,15 +86,37 @@ contract RecurringCollector is EIP712, GraphDirectory, Authorizable, IRecurringC
11186
* @dev Caller must be the data service for the agreement.
11287
*/
11388
function cancel(address payer, address serviceProvider, bytes16 agreementId) external {
89+
_cancel(msg.sender, payer, serviceProvider, agreementId);
90+
}
91+
92+
/**
93+
* @notice Upgrade an indexing agreement.
94+
* See {IRecurringCollector.upgrade}.
95+
* @dev Caller must be the data service the RCV was issued to.
96+
*/
97+
function upgrade(
98+
bytes16 oldAgreementId,
99+
SignedRCV memory signedRCV
100+
) external onlyDataService(signedRCV.rcv.dataService) {
101+
_cancel(signedRCV.rcv.dataService, signedRCV.rcv.payer, signedRCV.rcv.serviceProvider, oldAgreementId);
102+
_accept(signedRCV);
103+
AgreementKey memory oldKey = AgreementKey({
104+
dataService: signedRCV.rcv.dataService,
105+
payer: signedRCV.rcv.payer,
106+
serviceProvider: signedRCV.rcv.serviceProvider,
107+
agreementId: oldAgreementId
108+
});
109+
AgreementData memory oldAgreement = _getAgreement(oldKey);
114110
AgreementKey memory key = AgreementKey({
115-
dataService: msg.sender,
116-
payer: payer,
117-
serviceProvider: serviceProvider,
118-
agreementId: agreementId
111+
dataService: signedRCV.rcv.dataService,
112+
payer: signedRCV.rcv.payer,
113+
serviceProvider: signedRCV.rcv.serviceProvider,
114+
agreementId: signedRCV.rcv.agreementId
119115
});
120116
AgreementData storage agreement = _getForUpdateAgreement(key);
121-
require(agreement.acceptedAt > 0, RecurringCollectorAgreementNeverAccepted(key));
122-
agreement.acceptedAt = CANCELED;
117+
agreement.acceptedAt = oldAgreement.acceptedAt;
118+
agreement.lastCollectionAt = oldAgreement.lastCollectionAt;
119+
agreement.updatedFromAgreementId = oldAgreementId;
123120
}
124121

125122
/**
@@ -185,6 +182,53 @@ contract RecurringCollector is EIP712, GraphDirectory, Authorizable, IRecurringC
185182
return _params.tokens;
186183
}
187184

185+
/**
186+
* @notice See {IRecurringCollector.accept}
187+
*/
188+
function _accept(SignedRCV memory _signedRCV) private {
189+
require(
190+
_signedRCV.rcv.acceptDeadline >= block.timestamp,
191+
RecurringCollectorAgreementAcceptanceElapsed(_signedRCV.rcv.acceptDeadline)
192+
);
193+
194+
// check that the voucher is signed by the payer (or proxy)
195+
_requireAuthorizedRCVSigner(_signedRCV);
196+
197+
AgreementKey memory key = AgreementKey({
198+
dataService: _signedRCV.rcv.dataService,
199+
payer: _signedRCV.rcv.payer,
200+
serviceProvider: _signedRCV.rcv.serviceProvider,
201+
agreementId: _signedRCV.rcv.agreementId
202+
});
203+
AgreementData storage agreement = _getForUpdateAgreement(key);
204+
// check that the agreement is not already accepted
205+
require(agreement.acceptedAt == 0, RecurringCollectorAgreementAlreadyAccepted(key));
206+
207+
// accept the agreement
208+
agreement.acceptedAt = block.timestamp;
209+
// FIX-ME: These need to be validated to something that makes sense for the contract
210+
agreement.duration = _signedRCV.rcv.duration;
211+
agreement.maxInitialTokens = _signedRCV.rcv.maxInitialTokens;
212+
agreement.maxOngoingTokensPerSecond = _signedRCV.rcv.maxOngoingTokensPerSecond;
213+
agreement.minSecondsPerCollection = _signedRCV.rcv.minSecondsPerCollection;
214+
agreement.maxSecondsPerCollection = _signedRCV.rcv.maxSecondsPerCollection;
215+
}
216+
217+
/**
218+
* @notice See {IRecurringCollector.cancel}
219+
*/
220+
function _cancel(address _dataService, address _payer, address _serviceProvider, bytes16 _agreementId) private {
221+
AgreementKey memory key = AgreementKey({
222+
dataService: _dataService,
223+
payer: _payer,
224+
serviceProvider: _serviceProvider,
225+
agreementId: _agreementId
226+
});
227+
AgreementData storage agreement = _getForUpdateAgreement(key);
228+
require(agreement.acceptedAt > 0, RecurringCollectorAgreementNeverAccepted(key));
229+
agreement.acceptedAt = CANCELED;
230+
}
231+
188232
/**
189233
* @notice Requires that the agreement is valid for collection.
190234
*/
@@ -257,4 +301,11 @@ contract RecurringCollector is EIP712, GraphDirectory, Authorizable, IRecurringC
257301
function _getForUpdateAgreement(AgreementKey memory _key) private view returns (AgreementData storage) {
258302
return agreements[_key.dataService][_key.payer][_key.serviceProvider][_key.agreementId];
259303
}
304+
305+
/**
306+
* @notice Gets an agreement.
307+
*/
308+
function _getAgreement(AgreementKey memory _key) private view returns (AgreementData memory) {
309+
return agreements[_key.dataService][_key.payer][_key.serviceProvider][_key.agreementId];
310+
}
260311
}

Diff for: packages/subgraph-service/contracts/SubgraphService.sol

+53-1
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,52 @@ contract SubgraphService is
612612
);
613613
}
614614

615+
function upgradeIndexingAgreement(
616+
bytes16 oldAgreementId,
617+
IRecurringCollector.SignedRCV calldata signedRCV
618+
)
619+
external
620+
whenNotPaused
621+
onlyAuthorizedForProvision(signedRCV.rcv.serviceProvider)
622+
onlyValidProvision(signedRCV.rcv.serviceProvider)
623+
onlyRegisteredIndexer(signedRCV.rcv.serviceProvider)
624+
{
625+
require(
626+
signedRCV.rcv.dataService == address(this),
627+
SubgraphServiceIndexingAgreementDataServiceMismatch(signedRCV.rcv.dataService)
628+
);
629+
630+
_cancelIndexingAgreement(signedRCV.rcv.payer, signedRCV.rcv.serviceProvider, oldAgreementId);
631+
IndexingAgreementKey memory oldKey = IndexingAgreementKey({
632+
indexer: signedRCV.rcv.serviceProvider,
633+
payer: signedRCV.rcv.payer,
634+
agreementId: oldAgreementId
635+
});
636+
IndexingAgreementData memory oldAgreement = _getIndexingAgreement(oldKey);
637+
638+
RCVIndexingAgreementMetadata memory metadata = _decodeRCVMetadata(signedRCV.rcv.metadata);
639+
Allocation.State memory allocation = _allocations.get(oldAgreement.allocationId);
640+
require(
641+
allocation.subgraphDeploymentId == metadata.subgraphDeploymentId,
642+
SubgraphServiceIndexingAgreementDeploymentIdMismatch(
643+
metadata.subgraphDeploymentId,
644+
oldAgreement.allocationId,
645+
allocation.subgraphDeploymentId
646+
)
647+
);
648+
649+
_acceptIndexingAgreement(oldAgreement.allocationId, signedRCV, metadata);
650+
IndexingAgreementKey memory key = IndexingAgreementKey({
651+
indexer: signedRCV.rcv.serviceProvider,
652+
payer: signedRCV.rcv.payer,
653+
agreementId: signedRCV.rcv.agreementId
654+
});
655+
IndexingAgreementData storage agreement = _getForUpdateIndexingAgreement(key);
656+
657+
agreement.acceptedAt = oldAgreement.acceptedAt;
658+
agreement.lastCollectionAt = oldAgreement.lastCollectionAt;
659+
}
660+
615661
/**
616662
* @notice Cancel an indexing agreement by indexer / operator.
617663
* See {ISubgraphService.cancelIndexingAgreement}.
@@ -795,7 +841,7 @@ contract SubgraphService is
795841
collectionSeconds -= agreement.lastCollectionAt > 0 ? agreement.lastCollectionAt : agreement.acceptedAt;
796842
agreement.lastCollectionAt = block.timestamp;
797843

798-
// FIX-ME: this is bad because it encourages people to collect at max seconds allowed to maximize collection.
844+
// FIX-ME: this is bad because it encourages indexers to collect at max seconds allowed to maximize collection.
799845
return collectionSeconds * (termsV1.tokensPerSecond + termsV1.tokensPerEntityPerSecond * _entities);
800846
}
801847

@@ -853,6 +899,12 @@ contract SubgraphService is
853899
return indexingAgreements[_key.indexer][_key.payer][_key.agreementId];
854900
}
855901

902+
function _getIndexingAgreement(
903+
IndexingAgreementKey memory _key
904+
) private view returns (IndexingAgreementData memory) {
905+
return indexingAgreements[_key.indexer][_key.payer][_key.agreementId];
906+
}
907+
856908
function _getForUpdateIndexingAgreementTermsV1(
857909
IndexingAgreementKey memory _key
858910
) private view returns (IndexingAgreementTermsV1 storage) {

Diff for: packages/subgraph-service/contracts/interfaces/ISubgraphService.sol

+8
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,14 @@ interface ISubgraphService is IDataServiceFees {
484484
*/
485485
function acceptIndexingAgreement(address allocationId, IRecurringCollector.SignedRCV calldata signedRCV) external;
486486

487+
/**
488+
* @notice Upgrade an indexing agreement.
489+
*/
490+
function upgradeIndexingAgreement(
491+
bytes16 oldAgreementId,
492+
IRecurringCollector.SignedRCV calldata signedRCV
493+
) external;
494+
487495
/**
488496
* @notice Cancel an indexing agreement by indexer / operator.
489497
*/

0 commit comments

Comments
 (0)