Skip to content
This repository was archived by the owner on Sep 26, 2023. It is now read-only.

Commit fce4426

Browse files
authored
Dealmaking bugfix4 (#156)
Various fixes and enhancements on dealmaking: csv support, force send support, deal tracking bug
1 parent f056892 commit fce4426

14 files changed

Lines changed: 463 additions & 437 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,3 +120,4 @@ outDir
120120
nunitresults.xml
121121
config/cars/
122122

123+
.DS_Store

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,8 +326,10 @@ Options:
326326
-c, --cron-schedule <cronschedule> Optional cron to send deals at interval. Use double quote to wrap the format containing spaces.
327327
-x, --cron-max-deals <cronmaxdeals> When cron schedule specified, limit the total number of deals across entire cron, per SP.
328328
-xp, --cron-max-pending-deals <cronmaxpendingdeals> When cron schedule specified, limit the total number of pending deals determined by dealtracking service, per SP.
329-
-l, --file-list-path <filelistpath> Absolute path to a txt file that will limit to replicate only from the list. Must be visible by deal replication worker.
329+
-l, --file-list-path <filelistpath> Path to a txt file that will limit to replicate only from the list. Must be visible by deal replication worker.
330330
-n, --notes <notes> Any notes or tag want to store along the replication request, for tracking purpose.
331+
-csv, --output-csv <outputCsv> Print CSV to specified folder after done. Folder must exist on worker.
332+
-f, --force Force resend even if this pieceCID have been proposed / active by the provider. (default: false)
331333
-h, --help display help for command
332334
```
333335

src/common/Datastore.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,8 @@ export default class Datastore {
184184
cronMaxPendingDeals: Schema.Types.Number,
185185
fileListPath: Schema.Types.String,
186186
notes: Schema.Types.String,
187+
csvOutputDir: Schema.Types.String,
188+
isForced: Schema.Types.Boolean,
187189
errorMessage: Schema.Types.String
188190
}, {
189191
timestamps: true

src/common/GenerateCsv.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import path from 'path';
2+
import fs from 'fs-extra';
3+
import DealReplicationWorker from '../replication/DealReplicationWorker';
4+
import Datastore from './Datastore';
5+
import ObjectsToCsv from 'objects-to-csv';
6+
7+
/**
8+
* Export replication request to CSV
9+
*/
10+
export default class GenerateCsv {
11+
public static async generate (id: string, outDir: string): Promise<string> {
12+
let msg = '';
13+
fs.mkdirpSync(outDir);
14+
const replicationRequest = await Datastore.ReplicationRequestModel.findById(id);
15+
if (replicationRequest) {
16+
const providers = DealReplicationWorker.generateProvidersList(replicationRequest.storageProviders);
17+
for (let j = 0; j < providers.length; j++) {
18+
const provider = providers[j];
19+
const deals = await Datastore.DealStateModel.find({
20+
replicationRequestId: id,
21+
provider: provider,
22+
state: { $nin: ['slashed', 'error', 'expired', 'proposal_expired'] }
23+
});
24+
let urlPrefix = replicationRequest.urlPrefix;
25+
if (!urlPrefix.endsWith('/')) {
26+
urlPrefix += '/';
27+
}
28+
29+
if (deals.length > 0) {
30+
const csvRow = [];
31+
for (let i = 0; i < deals.length; i++) {
32+
const deal = deals[i];
33+
csvRow.push({
34+
miner_id: deal.provider,
35+
deal_cid: deal.dealCid,
36+
filename: `${deal.pieceCid}.car`,
37+
data_cid: deal.dataCid,
38+
piece_cid: deal.pieceCid,
39+
start_epoch: deal.startEpoch,
40+
full_url: `${urlPrefix}${deal.pieceCid}.car`
41+
});
42+
}
43+
const csv = new ObjectsToCsv(csvRow);
44+
let fileListFilename = '';
45+
if (replicationRequest.fileListPath) {
46+
fileListFilename += '_' + path.parse(replicationRequest.fileListPath).name;
47+
}
48+
const filename = path.join(outDir, `${provider}${fileListFilename}_${id}.csv`);
49+
await csv.toDisk(filename);
50+
msg += `CSV saved to ${filename}\n`;
51+
} else {
52+
msg += `No deal found to export in ${id}\n`;
53+
}
54+
}
55+
} else {
56+
msg = `Replication request not found ${id}`;
57+
}
58+
return msg;
59+
}
60+
}

src/common/model/ReplicationRequest.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,7 @@ export default interface ReplicationRequest {
2121
cronMaxPendingDeals?: number, // per SP pending total with cron considered
2222
fileListPath?: string, // limit to replicate only from the list in a txt file
2323
notes?: string, // any notes or tag want to store along the replication request, for tracking purpose
24+
csvOutputDir?: string, // folder to print CSV to, undefined to skip the CSV
25+
isForced: boolean,
2426
errorMessage?: string
2527
}

src/deal-tracking/DealTrackingService.ts

Lines changed: 30 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -141,61 +141,6 @@ export default class DealTrackingService extends BaseService {
141141
} while (response.data['result']['deals'].length > 0);
142142
}
143143

144-
/**
145-
* @param client
146-
* @param lastDeal
147-
*/
148-
/* Temporarily disabled in favor of filscan for more information
149-
private async insertDealFromFilfox(client: string, lastDeal: number): Promise<void> {
150-
this.logger.debug('Inserting new deals from filfox', { client, lastDeal });
151-
let page = 0;
152-
let response;
153-
do {
154-
let breakOuter = false;
155-
// Exponential retry as filfox can throttle us
156-
response = await retry(
157-
async () => {
158-
const url = `https://filfox.info/api/v1/deal/list?address=${client}&pageSize=100&page=${page}`;
159-
this.logger.debug(`Fetching from ${url}`);
160-
let r;
161-
try {
162-
r = await axios.get(url);
163-
} catch (e) {
164-
this.logger.warn(e);
165-
throw e;
166-
}
167-
return r;
168-
}, {
169-
retries: 3,
170-
minTimeout: 60_000
171-
}
172-
);
173-
this.logger.debug(`Received ${response.data['deals'].length} deal entries.`);
174-
for (const deal of response.data['deals']) {
175-
if (deal['id'] <= lastDeal) {
176-
breakOuter = true;
177-
break;
178-
}
179-
await Datastore.DealStateModel.updateOne({
180-
dealId: deal['id']
181-
}, {
182-
$setOnInsert: {
183-
client,
184-
provider: deal['provider'],
185-
dealId: deal['id'],
186-
state: 'published'
187-
}
188-
}, {
189-
upsert: true
190-
});
191-
}
192-
if (breakOuter) {
193-
break;
194-
}
195-
page += 1;
196-
} while (response.data['deals'].length > 0);
197-
}
198-
*/
199144
private async markExpiredDeals (client: string): Promise<void> {
200145
const chainHeight = HeightFromCurrentTime() - 120;
201146
let modified = (await Datastore.DealStateModel.updateMany({
@@ -254,37 +199,37 @@ export default class DealTrackingService extends BaseService {
254199
await Datastore.DealStateModel.findByIdAndUpdate(dealState.id, {
255200
state: 'slashed'
256201
});
257-
return;
258-
}
259-
const result = response.data.result;
260-
const expiration: number = result.Proposal.EndEpoch;
261-
const slashed = result.State.SlashEpoch > 0;
262-
const pieceCid = result.Proposal.PieceCID['/'];
263-
const dealActive = result.State.SectorStartEpoch > 0;
264-
if (slashed) {
265-
await Datastore.DealStateModel.findByIdAndUpdate(dealState.id, {
266-
pieceCid, expiration, state: 'slashed'
267-
});
268-
this.logger.warn(`Deal ${dealState.dealId} is slashed.`);
269-
} else if (dealActive) {
270-
await Datastore.DealStateModel.findByIdAndUpdate(dealState.id, {
271-
pieceCid, expiration, state: 'active'
272-
});
273-
this.logger.info(`Deal ${dealState.dealId} is active on chain.`);
274-
if (dealState.dealCid && dealState.dealCid !== '') {
275-
await MetricEmitter.Instance().emit({
276-
type: 'deal_active',
277-
values: {
278-
pieceCid: dealState.pieceCid,
279-
pieceSize: dealState.pieceSize,
280-
dataCid: dealState.dataCid,
281-
provider: dealState.provider,
282-
client: dealState.client,
283-
verified: dealState.verified,
284-
duration: dealState.duration,
285-
price: dealState.price
286-
}
202+
} else {
203+
const result = response.data.result;
204+
const expiration: number = result.Proposal.EndEpoch;
205+
const slashed = result.State.SlashEpoch > 0;
206+
const pieceCid = result.Proposal.PieceCID['/'];
207+
const dealActive = result.State.SectorStartEpoch > 0;
208+
if (slashed) {
209+
await Datastore.DealStateModel.findByIdAndUpdate(dealState.id, {
210+
pieceCid, expiration, state: 'slashed'
211+
});
212+
this.logger.warn(`Deal ${dealState.dealId} is slashed.`);
213+
} else if (dealActive) {
214+
await Datastore.DealStateModel.findByIdAndUpdate(dealState.id, {
215+
pieceCid, expiration, state: 'active'
287216
});
217+
this.logger.info(`Deal ${dealState.dealId} is active on chain.`);
218+
if (dealState.dealCid && dealState.dealCid !== '') {
219+
await MetricEmitter.Instance().emit({
220+
type: 'deal_active',
221+
values: {
222+
pieceCid: dealState.pieceCid,
223+
pieceSize: dealState.pieceSize,
224+
dataCid: dealState.dataCid,
225+
provider: dealState.provider,
226+
client: dealState.client,
227+
verified: dealState.verified,
228+
duration: dealState.duration,
229+
price: dealState.price
230+
}
231+
});
232+
}
288233
}
289234
}
290235
}

0 commit comments

Comments
 (0)