Skip to content
This repository was archived by the owner on Dec 27, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 56 additions & 36 deletions modules/browser-node/src/services/store.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
ChannelDispute,
ChannelUpdate,
CoreChannelState,
CoreTransferState,
FullChannelState,
Expand Down Expand Up @@ -42,6 +43,7 @@ const getStoreName = (publicIdentifier: string) => {
};
const NON_NAMESPACED_STORE = "VectorIndexedDBDatabase";
class VectorIndexedDBDatabase extends Dexie {
updates: Dexie.Table<ChannelUpdate, string>;
channels: Dexie.Table<FullChannelState, string>;
transfers: Dexie.Table<StoredTransfer, string>;
transactions: Dexie.Table<StoredTransaction, string>;
Expand Down Expand Up @@ -111,29 +113,38 @@ class VectorIndexedDBDatabase extends Dexie {

// Using a temp table (transactions2) to migrate which column is the primary key
// (transactionHash -> id)
this.version(5).stores({
withdrawCommitment: "transferId,channelAddress,transactionHash",
transactions2: "id, transactionHash",
}).upgrade(async tx => {
const transactions = await tx.table("transactions").toArray();
await tx.table("transactions2").bulkAdd(transactions);
});
this.version(5)
.stores({
withdrawCommitment: "transferId,channelAddress,transactionHash",
transactions2: "id, transactionHash",
})
.upgrade(async (tx) => {
const transactions = await tx.table("transactions").toArray();
await tx.table("transactions2").bulkAdd(transactions);
});

this.version(6).stores({
transactions: null
transactions: null,
});

this.version(7).stores({
transactions: "id, transactionHash"
}).upgrade(async tx => {
const transactions2 = await tx.table("transactions2").toArray();
await tx.table("transactions").bulkAdd(transactions2);
});
this.version(7)
.stores({
transactions: "id, transactionHash",
})
.upgrade(async (tx) => {
const transactions2 = await tx.table("transactions2").toArray();
await tx.table("transactions").bulkAdd(transactions2);
});

this.version(8).stores({
transactions2: null
transactions2: null,
});

this.version(9).stores({
updates: "id.id, [channelAddress+nonce]",
});

this.updates = this.table("updates");
this.channels = this.table("channels");
this.transfers = this.table("transfers");
this.transactions = this.table("transactions");
Expand Down Expand Up @@ -245,8 +256,9 @@ export class BrowserStore implements IEngineStore, IChainServiceStore {
}

async saveChannelState(channelState: FullChannelState, transfer?: FullTransferState): Promise<void> {
await this.db.transaction("rw", this.db.channels, this.db.transfers, async () => {
await this.db.transaction("rw", this.db.channels, this.db.transfers, this.db.updates, async () => {
await this.db.channels.put(channelState);
await this.db.updates.put(channelState.latestUpdate);
if (channelState.latestUpdate.type === UpdateType.create) {
await this.db.transfers.put({
...transfer!,
Expand All @@ -264,6 +276,11 @@ export class BrowserStore implements IEngineStore, IChainServiceStore {
});
}

async getUpdateById(id: string): Promise<ChannelUpdate | undefined> {
const update = await this.db.updates.get(id);
return update;
}

async getChannelStates(): Promise<FullChannelState[]> {
const channels = await this.db.channels.toArray();
return channels;
Expand Down Expand Up @@ -356,7 +373,7 @@ export class BrowserStore implements IEngineStore, IChainServiceStore {
}

async getTransactionById(onchainTransactionId: string): Promise<StoredTransaction | undefined> {
return await this.db.transactions.get({ id: onchainTransactionId })
return await this.db.transactions.get({ id: onchainTransactionId });
}

async getActiveTransactions(): Promise<StoredTransaction[]> {
Expand All @@ -383,30 +400,33 @@ export class BrowserStore implements IEngineStore, IChainServiceStore {
attempts.push({
// TransactionResponse fields (defined when submitted)
gasLimit: response.gasLimit.toString(),
gasPrice: response.gasPrice.toString(),
gasPrice: response.gasPrice.toString(),
transactionHash: response.hash,

createdAt: new Date(),
} as StoredTransactionAttempt);

await this.db.transactions.put({
id: onchainTransactionId,

//// Helper fields
channelAddress,
status: StoredTransactionStatus.submitted,
reason,

//// Provider fields
// Minimum fields (should always be defined)
to: response.to!,
from: response.from,
data: response.data,
value: response.value.toString(),
chainId: response.chainId,
nonce: response.nonce,
attempts,
} as StoredTransaction, onchainTransactionId);
await this.db.transactions.put(
{
id: onchainTransactionId,

//// Helper fields
channelAddress,
status: StoredTransactionStatus.submitted,
reason,

//// Provider fields
// Minimum fields (should always be defined)
to: response.to!,
from: response.from,
data: response.data,
value: response.value.toString(),
chainId: response.chainId,
nonce: response.nonce,
attempts,
} as StoredTransaction,
onchainTransactionId,
);
}

async saveTransactionReceipt(onchainTransactionId: string, receipt: TransactionReceipt): Promise<void> {
Expand Down
23 changes: 13 additions & 10 deletions modules/engine/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1014,7 +1014,9 @@ export class VectorEngine implements IVectorEngine {

private async addTransactionToCommitment(
params: EngineParams.AddTransactionToCommitment,
): Promise<Result<ChannelRpcMethodsResponsesMap[typeof ChannelRpcMethods.chan_addTransactionToCommitment], EngineError>> {
): Promise<
Result<ChannelRpcMethodsResponsesMap[typeof ChannelRpcMethods.chan_addTransactionToCommitment], EngineError>
> {
const method = "addTransactionToCommitment";
const methodId = getRandomBytes32();
this.logger.info({ params, method, methodId }, "Method started");
Expand Down Expand Up @@ -1588,15 +1590,16 @@ export class VectorEngine implements IVectorEngine {
channelAddress: string,
retryCount = 5,
) {
let result: Result<T> | undefined;
for (let i = 0; i < retryCount; i++) {
result = await fn();
if (!result.isError) {
return result;
}
this.logger.warn({ attempt: i, error: result.getError().message, channelAddress }, "Protocol method failed");
await delay(500);
}
const result = await fn();
// let result: Result<T> | undefined;
// for (let i = 0; i < retryCount; i++) {
// result = await fn();
// if (!result.isError) {
// return result;
// }
// this.logger.warn({ attempt: i, error: result.getError().message, channelAddress }, "Protocol method failed");
// await delay(500);
// }
return result as Result<T, ProtocolError>;
}

Expand Down
3 changes: 2 additions & 1 deletion modules/protocol/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
"evt": "1.9.12",
"fastq": "1.11.0",
"pino": "6.11.1",
"tty": "1.0.1"
"tty": "1.0.1",
"uuid": "8.3.2"
},
"devDependencies": {
"@types/chai": "4.2.15",
Expand Down
2 changes: 2 additions & 0 deletions modules/protocol/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export class ValidationError extends ProtocolError {
TransferTimeoutBelowMin: `Transfer timeout below minimum of ${MINIMUM_TRANSFER_TIMEOUT.toString()}s`,
TransferTimeoutAboveMax: `Transfer timeout above maximum of ${MAXIMUM_TRANSFER_TIMEOUT.toString()}s`,
UnrecognizedType: "Unrecognized update type",
UpdateIdSigInvalid: "Update id signature is invalid",
} as const;

constructor(
Expand Down Expand Up @@ -135,6 +136,7 @@ export class QueuedUpdateError extends ProtocolError {
StoreFailure: "Store method failed",
TransferNotActive: "Transfer not found in activeTransfers",
UnhandledPromise: "Unhandled promise rejection encountered",
UpdateIdSigInvalid: "Update id signature is invalid",
} as const;

// TODO: improve error from result
Expand Down
6 changes: 3 additions & 3 deletions modules/protocol/src/sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ type UpdateResult = {
};

export type SelfUpdateResult = UpdateResult & {
successfullyApplied: boolean;
successfullyApplied: "synced" | "executed" | "previouslyExecuted";
};

export async function outbound(
Expand Down Expand Up @@ -178,7 +178,7 @@ export async function outbound(
updatedChannel: syncedChannel,
updatedActiveTransfers: syncedActiveTransfers,
updatedTransfer: syncedTransfer,
successfullyApplied: false,
successfullyApplied: "synced",
});
}

Expand Down Expand Up @@ -209,7 +209,7 @@ export async function outbound(
updatedChannel: { ...updatedChannel, latestUpdate: counterpartyUpdate },
updatedTransfers: updatedActiveTransfers,
updatedTransfer,
successfullyApplied: true,
successfullyApplied: "executed",
});
}

Expand Down
10 changes: 6 additions & 4 deletions modules/protocol/src/testing/sync.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
createTestChannelUpdateWithSigners,
createTestChannelStateWithSigners,
createTestFullHashlockTransferState,
getRandomBytes32,
createTestUpdateParams,
mkAddress,
mkSig,
Expand All @@ -14,7 +13,6 @@ import {
MemoryMessagingService,
getTestLoggers,
createTestChannelUpdate,
createTestChannelState,
} from "@connext/vector-utils";
import {
UpdateType,
Expand Down Expand Up @@ -459,6 +457,7 @@ describe("outbound", () => {
let validateParamsAndApplyStub: Sinon.SinonStub;
// called during sync
let validateAndApplyInboundStub: Sinon.SinonStub;
let validateUpdateIdSignatureStub: Sinon.SinonStub;

beforeEach(async () => {
signers = Array(2)
Expand All @@ -476,6 +475,9 @@ describe("outbound", () => {

// Stub out all signature validation
validateUpdateSignatureStub = Sinon.stub(vectorUtils, "validateChannelSignatures").resolves(Result.ok(undefined));
validateUpdateIdSignatureStub = Sinon.stub(vectorUtils, "validateChannelUpdateIdSignature").resolves(
Result.ok(undefined),
);
});

afterEach(() => {
Expand Down Expand Up @@ -865,9 +867,9 @@ describe("outbound", () => {
log,
);

// Verify the update was successfully sent + retried
// Verify the update was successfully sent + synced
expect(res.getError()).to.be.undefined;
expect(res.getValue().successfullyApplied).to.be.false;
expect(res.getValue().successfullyApplied).to.be.eq("synced");
expect(res.getValue().updatedChannel).to.be.containSubset({
nonce: toSync.nonce,
latestUpdate: toSync,
Expand Down
10 changes: 10 additions & 0 deletions modules/protocol/src/testing/validate.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ describe("validateUpdateParams", () => {

// Declare all mocks
let chainReader: Sinon.SinonStubbedInstance<VectorChainReader>;
let validateUpdateIdSignatureStub: Sinon.SinonStub;

// Create helpers to create valid contexts
const createValidSetupContext = () => {
Expand Down Expand Up @@ -198,6 +199,10 @@ describe("validateUpdateParams", () => {
chainReader = Sinon.createStubInstance(VectorChainReader);
chainReader.getChannelAddress.resolves(Result.ok(channelAddress));
chainReader.create.resolves(Result.ok(true));

validateUpdateIdSignatureStub = Sinon.stub(vectorUtils, "validateChannelUpdateIdSignature").resolves(
Result.ok(undefined),
);
});

afterEach(() => {
Expand Down Expand Up @@ -795,6 +800,7 @@ describe("validateAndApplyInboundUpdate", () => {
let chainReader: Sinon.SinonStubbedInstance<VectorChainReader>;
let validateParamsAndApplyUpdateStub: Sinon.SinonStub;
let validateChannelUpdateSignaturesStub: Sinon.SinonStub;
let validateUpdateIdSignatureStub: Sinon.SinonStub;
let generateSignedChannelCommitmentStub: Sinon.SinonStub;
let applyUpdateStub: Sinon.SinonStub;
let externalValidationStub: {
Expand Down Expand Up @@ -834,6 +840,7 @@ describe("validateAndApplyInboundUpdate", () => {

// Need for double signed and single signed
validateChannelUpdateSignaturesStub.resolves(Result.ok(undefined));
validateUpdateIdSignatureStub.resolves(Result.ok(undefined));

// Needed for double signed
chainReader.resolve.resolves(Result.ok({ to: [updatedChannel.alice, updatedChannel.bob], amount: ["10", "2"] }));
Expand Down Expand Up @@ -866,6 +873,9 @@ describe("validateAndApplyInboundUpdate", () => {
validateChannelUpdateSignaturesStub = Sinon.stub(vectorUtils, "validateChannelSignatures").resolves(
Result.ok(undefined),
);
validateUpdateIdSignatureStub = Sinon.stub(vectorUtils, "validateChannelUpdateIdSignature").resolves(
Result.ok(undefined),
);
generateSignedChannelCommitmentStub = Sinon.stub(vectorUtils, "generateSignedChannelCommitment");
applyUpdateStub = Sinon.stub(vectorUpdate, "applyUpdate");
externalValidationStub = {
Expand Down
2 changes: 1 addition & 1 deletion modules/protocol/src/testing/vector.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe("Vector", () => {
storeService.getChannelStates.resolves([]);
// Mock sync outbound
Sinon.stub(vectorSync, "outbound").resolves(
Result.ok({ updatedChannel: createTestChannelState(UpdateType.setup).channel, successfullyApplied: true }),
Result.ok({ updatedChannel: createTestChannelState(UpdateType.setup).channel, successfullyApplied: "executed" }),
);
});

Expand Down
4 changes: 3 additions & 1 deletion modules/protocol/src/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ function generateSetupUpdate(
meta: params.details.meta ?? {},
},
assetId: AddressZero,
id: params.id,
};

return unsigned;
Expand Down Expand Up @@ -597,7 +598,7 @@ function generateBaseUpdate<T extends UpdateType>(
params: UpdateParams<T>,
signer: IChannelSigner,
initiatorIdentifier: string,
): Pick<ChannelUpdate<T>, "channelAddress" | "nonce" | "fromIdentifier" | "toIdentifier" | "type"> {
): Pick<ChannelUpdate<T>, "channelAddress" | "nonce" | "fromIdentifier" | "toIdentifier" | "type" | "id"> {
const isInitiator = signer.publicIdentifier === initiatorIdentifier;
const counterparty = signer.publicIdentifier === state.bobIdentifier ? state.aliceIdentifier : state.bobIdentifier;
return {
Expand All @@ -606,6 +607,7 @@ function generateBaseUpdate<T extends UpdateType>(
type: params.type,
fromIdentifier: initiatorIdentifier,
toIdentifier: isInitiator ? counterparty : signer.publicIdentifier,
id: params.id,
};
}

Expand Down
Loading