Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

Commit

Permalink
Merge pull request #869 from trufflesuite/feat/filecoin-deal-expiration
Browse files Browse the repository at this point in the history
  • Loading branch information
mikeseese authored Mar 26, 2021
2 parents 0ca3186 + 7e40480 commit 8e487ee
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 13 deletions.
28 changes: 25 additions & 3 deletions src/chains/filecoin/filecoin/src/blockchain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import { DealInfo } from "./things/deal-info";
import { StartDealParams } from "./things/start-deal-params";
import {
dealIsInProcess,
StorageDealStatus
StorageDealStatus,
nextSuccessfulState
} from "./types/storage-deal-status";
import IPFSServer from "./ipfs-server";
import dagCBOR from "ipld-dag-cbor";
Expand Down Expand Up @@ -149,7 +150,8 @@ export default class Blockchain extends Emittery.Typed<
this.signedMessagesManager
);
this.dealInfoManager = await DealInfoManager.initialize(
this.#database.deals!
this.#database.deals!,
this.#database.dealExpirations!
);

const controllableAccounts = await this.accountManager.getControllableAccounts();
Expand Down Expand Up @@ -655,6 +657,21 @@ export default class Blockchain extends Emittery.Typed<
this.emit("dealUpdate", deal);
}

// Process deal expirations
const activeDeals = currentDeals.filter(
deal => deal.state === StorageDealStatus.Active
);
for (const deal of activeDeals) {
const expirationTipset = await this.dealInfoManager!.getDealExpiration(
deal.proposalCid
);
if (expirationTipset !== null && newTipset.height > expirationTipset) {
deal.state = StorageDealStatus.Expired;
await this.dealInfoManager!.updateDealInfo(deal);
this.emit("dealUpdate", deal);
}
}

this.logLatestTipset();

this.emit("tipset", newTipset);
Expand Down Expand Up @@ -798,7 +815,12 @@ export default class Blockchain extends Emittery.Typed<
dealId: currentDeals.length + 1
});

await this.dealInfoManager!.addDealInfo(deal);
// prepare future deal expiration
const activeTipsetHeight =
this.latestTipset().height + Object.keys(nextSuccessfulState).length - 1;
const expirationTipsetHeight = activeTipsetHeight + deal.duration;

await this.dealInfoManager!.addDealInfo(deal, expirationTipsetHeight);
this.emit("dealUpdate", deal);

// If we're automining, mine a new block. Note that this will
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Manager from "./manager";
import { LevelUp } from "levelup";
import { DealInfo, DealInfoConfig } from "../things/deal-info";
import { SerializedRootCID } from "../things/root-cid";
import { RootCID, SerializedRootCID } from "../things/root-cid";

const NOTFOUND = 404;

Expand All @@ -19,24 +19,32 @@ const NOTFOUND = 404;
export default class DealInfoManager extends Manager<DealInfo, DealInfoConfig> {
static Deals = Buffer.from("deals");

static async initialize(base: LevelUp) {
const manager = new DealInfoManager(base);
#dealExpirations: LevelUp;

static async initialize(base: LevelUp, dealExpirations: LevelUp) {
const manager = new DealInfoManager(base, dealExpirations);
return manager;
}

constructor(base: LevelUp) {
constructor(base: LevelUp, dealExpirations: LevelUp) {
super(base, DealInfo);
this.#dealExpirations = dealExpirations;
}

async updateDealInfo(deal: DealInfo) {
await super.set(deal.proposalCid.root.value, deal);
}

async addDealInfo(deal: DealInfo) {
async addDealInfo(deal: DealInfo, expirationTipsetHeight: number) {
await this.updateDealInfo(deal);
const cids = await this.getDealCids();
cids.push(deal.proposalCid.serialize());
await this.putDealCids(cids);

this.#dealExpirations.put(
Buffer.from(deal.proposalCid.root.value),
Buffer.from(`${expirationTipsetHeight}`)
);
}

async getDealCids(): Promise<Array<SerializedRootCID>> {
Expand Down Expand Up @@ -86,6 +94,20 @@ export default class DealInfoManager extends Manager<DealInfo, DealInfoConfig> {
}
}

async getDealExpiration(proposalId: RootCID): Promise<number | null> {
try {
const result = await this.#dealExpirations.get(
Buffer.from(proposalId.root.value)
);
return parseInt(result.toString(), 10);
} catch (e) {
if (e.status === NOTFOUND) {
return null;
}
throw e;
}
}

private async putDealCids(cids: Array<SerializedRootCID>): Promise<void> {
await this.base.put(DealInfoManager.Deals, JSON.stringify(cids));
}
Expand Down
5 changes: 5 additions & 0 deletions src/chains/filecoin/filecoin/src/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export default class Database extends Emittery {
public signedMessages: LevelUp | null = null;
public blockMessages: LevelUp | null = null;
public deals: LevelUp | null = null;
public dealExpirations: LevelUp | null = null;

#initialized: boolean = false;
get initialized() {
Expand Down Expand Up @@ -96,6 +97,7 @@ export default class Database extends Emittery {
this.signedMessages = sub(db, "m", levelupOptions);
this.blockMessages = sub(db, "bm", levelupOptions);
this.deals = sub(db, "d", levelupOptions);
this.dealExpirations = sub(db, "de", levelupOptions);

this.#initialized = true;
return this.emit("ready");
Expand Down Expand Up @@ -187,6 +189,9 @@ export default class Database extends Emittery {
if (this.deals) {
await this.deals.close();
}
if (this.dealExpirations) {
await this.dealExpirations.close();
}
}
return await this.#cleanupDirectory();
};
Expand Down
33 changes: 32 additions & 1 deletion src/chains/filecoin/filecoin/tests/api/filecoin/deals.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ describe("api", () => {
dealId: -1
});
const dealStatuses: StorageDealStatus[] = [];
const dealDuration = 5;

before(async () => {
ipfs = getIpfsClient();
Expand Down Expand Up @@ -100,7 +101,7 @@ describe("api", () => {
wallet: address,
miner: miners[0],
epochPrice: 2500n,
minBlocksDuration: 300
minBlocksDuration: dealDuration
});

const proposalCid = await client.clientStartDeal(proposal.serialize());
Expand Down Expand Up @@ -196,6 +197,36 @@ describe("api", () => {
);
}
});

it("expires a deal at the given duration", async () => {
let deals = await client.clientListDeals();
assert.strictEqual(deals.length, 1);
let deal: SerializedDealInfo = deals[0];
assert.strictEqual(deal.State, StorageDealStatus.Active);

const priorHead = await client.chainHead();
let currentHead = await client.chainHead();

while (
deal.State === StorageDealStatus.Active &&
currentHead.Height - priorHead.Height <= dealDuration
) {
await provider.send({
jsonrpc: "2.0",
id: "0",
method: "Ganache.MineTipset"
});

deal = (await client.clientListDeals())[0];
currentHead = await client.chainHead();
}

assert.strictEqual(deal.State, StorageDealStatus.Expired);
assert.strictEqual(
currentHead.Height,
priorHead.Height + dealDuration + 1
); // the duration is inclusive, thus the +1
});
});

describe("Filecoin.ClientFindData, Filecoin.ClientRetrieve, and Filecoin.ClientHasLocal", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,20 @@
import Manager from "./manager";
import { LevelUp } from "levelup";
import { DealInfo, DealInfoConfig } from "../things/deal-info";
import { SerializedRootCID } from "../things/root-cid";
import { RootCID, SerializedRootCID } from "../things/root-cid";
export default class DealInfoManager extends Manager<DealInfo, DealInfoConfig> {
#private;
static Deals: Buffer;
static initialize(base: LevelUp): Promise<DealInfoManager>;
constructor(base: LevelUp);
static initialize(
base: LevelUp,
dealExpirations: LevelUp
): Promise<DealInfoManager>;
constructor(base: LevelUp, dealExpirations: LevelUp);
updateDealInfo(deal: DealInfo): Promise<void>;
addDealInfo(deal: DealInfo): Promise<void>;
addDealInfo(deal: DealInfo, expirationTipsetHeight: number): Promise<void>;
getDealCids(): Promise<Array<SerializedRootCID>>;
getDeals(): Promise<Array<DealInfo>>;
getDealById(dealId: number): Promise<DealInfo | null>;
getDealExpiration(proposalId: RootCID): Promise<number | null>;
private putDealCids;
}
1 change: 1 addition & 0 deletions src/chains/filecoin/types/src/database.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export default class Database extends Emittery {
signedMessages: LevelUp | null;
blockMessages: LevelUp | null;
deals: LevelUp | null;
dealExpirations: LevelUp | null;
get initialized(): boolean;
/**
* The Database handles the creation of the database, and all access to it.
Expand Down

0 comments on commit 8e487ee

Please sign in to comment.