Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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

‎IndexingPaymentsTodo.md

Lines changed: 1 addition & 1 deletion
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~~.

‎packages/horizon/contracts/interfaces/IRecurringCollector.sol

Lines changed: 9 additions & 0 deletions
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.

‎packages/horizon/contracts/payments/collectors/RecurringCollector.sol

Lines changed: 84 additions & 33 deletions
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
}

‎packages/subgraph-service/contracts/SubgraphService.sol

Lines changed: 53 additions & 1 deletion
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) {

‎packages/subgraph-service/contracts/interfaces/ISubgraphService.sol

Lines changed: 8 additions & 0 deletions
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)

AltStyle によって変換されたページ (->オリジナル) /