Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
16 changes: 8 additions & 8 deletions packages/synapse-core/src/piece/internal/hasher.ts
Comment thread
snadrus marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export class Hasher {

/** Append bytes to the hasher. */
write(bytes: Uint8Array): this {
const { buffer, offset, layers } = this
const { buffer, layers } = this
const leaves = layers[0]
const { length } = bytes

Expand All @@ -51,16 +51,16 @@ export class Hasher {
}

// Not enough for a quad yet: stash in the buffer.
if (offset + length < buffer.length) {
buffer.set(bytes, offset)
if (this.offset + length < buffer.length) {
buffer.set(bytes, this.offset)
this.offset += length
this.bytesWritten += BigInt(length)
return this
}

// Fill the buffer to complete a quad, then process whole quads from `bytes`.
const bytesRequired = buffer.length - offset
buffer.set(bytes.subarray(0, bytesRequired), offset)
const bytesRequired = buffer.length - this.offset
buffer.set(bytes.subarray(0, bytesRequired), this.offset)
leaves.push(...split(fr32Expand(buffer)))

let readOffset = bytesRequired
Expand Down Expand Up @@ -88,14 +88,14 @@ export class Hasher {
}

private digestInto(output: Uint8Array, byteOffset: number, asMultihash: boolean): number {
const { buffer, layers, offset, bytesWritten } = this
const { buffer, layers, bytesWritten } = this

// Snapshot the layers so we don't mutate hasher state.
let [leaves, ...nodes] = layers

// If there's a partial quad in the buffer, zero-pad and absorb it.
if (offset > 0 || bytesWritten === 0n) {
leaves = [...leaves, ...split(fr32Expand(buffer.fill(0, offset)))]
if (this.offset > 0 || bytesWritten === 0n) {
leaves = [...leaves, ...split(fr32Expand(buffer.fill(0, this.offset)))]
}

const tree = build([leaves, ...nodes])
Expand Down
5 changes: 5 additions & 0 deletions packages/synapse-core/src/sp/add-pieces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@ export const AddPiecesPendingSchema = z.object({
pieceCount: z.number(),
addMessageOk: z.null(),
piecesAdded: z.literal(false),
/** Present once confirmed; equals txHash unless Curio replaced-by-fee. */
confirmedTxHash: zHex.optional(),
})

export const AddPiecesRejectedSchema = z.object({
Expand All @@ -185,6 +187,7 @@ export const AddPiecesRejectedSchema = z.object({
pieceCount: z.number(),
addMessageOk: z.null(),
piecesAdded: z.literal(false),
confirmedTxHash: zHex.optional(),
})

export const AddPiecesSuccessSchema = z.object({
Expand All @@ -195,6 +198,8 @@ export const AddPiecesSuccessSchema = z.object({
addMessageOk: z.literal(true),
piecesAdded: z.literal(true),
confirmedPieceIds: z.array(zNumberToBigInt),
/** Hash included on chain. Equals txHash unless Curio replaced-by-fee. Use `confirmedTxHash ?? txHash` for explorers/receipts. */
confirmedTxHash: zHex.optional(),
})

export type AddPiecesPending = z.infer<typeof AddPiecesPendingSchema>
Expand Down
7 changes: 7 additions & 0 deletions packages/synapse-core/src/sp/create-dataset-add-pieces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,13 @@ export namespace waitForCreateDataSetAddPieces {
pollInterval?: number
}
export type ReturnType = {
/** Original Location / wait-key hash (not necessarily the included on-chain hash). */
hash: string
/**
* Hash included on chain once confirmed. Differs from hash after replace-by-fee.
* For explorers and receipt lookups use `confirmedTxHash ?? hash`.
*/
confirmedTxHash?: string
dataSetId: bigint
piecesIds: bigint[]
}
Expand Down Expand Up @@ -227,6 +233,7 @@ export async function waitForCreateDataSetAddPieces(
})
return {
hash: createdDataset.createMessageHash,
confirmedTxHash: createdDataset.confirmedTxHash ?? addedPieces.confirmedTxHash,
dataSetId: createdDataset.dataSetId,
piecesIds: addedPieces.confirmedPieceIds,
}
Expand Down
5 changes: 5 additions & 0 deletions packages/synapse-core/src/sp/create-dataset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ export const CreateDataSetPendingSchema = z.object({
service: z.string(),
txStatus: z.union([z.literal('pending'), z.literal('confirmed')]),
ok: z.null(),
/** Present once confirmed; equals createMessageHash unless Curio replaced-by-fee. */
confirmedTxHash: zHex.optional(),
})

/**
Expand All @@ -176,6 +178,7 @@ export const CreateDataSetRejectedSchema = z.object({
service: z.string(),
txStatus: z.literal('rejected'),
ok: z.literal(false),
confirmedTxHash: zHex.optional(),
})

/**
Expand All @@ -188,6 +191,8 @@ export const CreateDataSetSuccessSchema = z.object({
txStatus: z.literal('confirmed'),
ok: z.literal(true),
dataSetId: zNumberToBigInt,
/** Hash included on chain. Equals createMessageHash unless Curio replaced-by-fee. Use `confirmedTxHash ?? createMessageHash` for explorers/receipts. */
confirmedTxHash: zHex.optional(),
})

export type CreateDataSetSuccess = z.infer<typeof CreateDataSetSuccessSchema>
Expand Down
7 changes: 6 additions & 1 deletion packages/synapse-core/src/sp/terminate-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ POST /pdp/data-sets/{id}/terminate
GET /pdp/data-sets/{id}/terminate (the status URL; valid immediately after the 202)
queued {terminationTxHash: "", fwssTerminated: null}
sent {terminationTxHash: "0x...", fwssTerminated: null}
done {terminationTxHash: "0x..." or "", fwssTerminated: true, serviceTerminationEpoch: 4567}
done {terminationTxHash: "0x..." or "", confirmedTxHash?: "0x...", fwssTerminated: true, serviceTerminationEpoch: 4567}
reverted if we get a hash and then get a 404, the tx was rejected
404 failed relays are discarded so the client can re-POST; also the
response for SP-initiated terminations (only client-requested ones
Expand All @@ -54,6 +54,8 @@ GET /pdp/data-sets/{id}/terminate (the status URL; valid immediately after the 2
competing terminate landed first; the goal state holds). When no terminate
tx ever lands, ours or anyone's (e.g. the SP is unable to send), there is no
terminal signal: the status stays queued and the poller runs to its timeout.
confirmedTxHash is the included on-chain hash when present; it differs from
terminationTxHash only if Curio replaced the original send by fee.
*/

/**
Expand Down Expand Up @@ -250,6 +252,7 @@ export const TerminateServiceStatusPendingSchema = z.object({
terminationTxHash: z.union([zHex, z.literal('')]),
fwssTerminated: z.null(),
serviceTerminationEpoch: z.null(),
confirmedTxHash: zHex.optional(),
})

/**
Expand All @@ -260,6 +263,8 @@ export const TerminateServiceStatusSuccessSchema = z.object({
terminationTxHash: z.union([zHex, z.literal('')]),
fwssTerminated: z.literal(true),
serviceTerminationEpoch: zNumberToBigInt,
/** Hash included on chain. Equals terminationTxHash unless Curio replaced-by-fee. Use `confirmedTxHash ?? terminationTxHash` for explorers/receipts. */
confirmedTxHash: zHex.optional(),
})

export type TerminateServiceStatusPending = z.infer<typeof TerminateServiceStatusPendingSchema>
Expand Down
2 changes: 2 additions & 0 deletions packages/synapse-sdk/src/storage/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -938,6 +938,7 @@ export class StorageContext {

return {
txHash: addPiecesResult.txHash as Hex,
...(confirmation.confirmedTxHash === undefined ? {} : { confirmedTxHash: confirmation.confirmedTxHash }),
pieceIds: confirmedPieceIds,
dataSetId: this._dataSetId,
isNewDataSet: false,
Expand All @@ -962,6 +963,7 @@ export class StorageContext {

return {
txHash: result.txHash as Hex,
...(confirmation.confirmedTxHash === undefined ? {} : { confirmedTxHash: confirmation.confirmedTxHash as Hex }),
pieceIds: confirmation.piecesIds,
dataSetId: this._dataSetId,
isNewDataSet: true,
Expand Down
9 changes: 8 additions & 1 deletion packages/synapse-sdk/src/storage/terminate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@ export async function terminateServiceFlow(
onHash: onSubmitted,
})
const event = extractPDPPaymentTerminatedEvent(receipt.logs)
return { txHash: receipt.transactionHash, dataSetId, endEpoch: event.args.endEpoch }
return {
txHash: receipt.transactionHash,
confirmedTxHash: receipt.transactionHash,
dataSetId,
endEpoch: event.args.endEpoch,
}
}

// Resolve (and, on the manager path, validate) the target first so a bad
Expand Down Expand Up @@ -86,6 +91,7 @@ export async function terminateServiceFlow(
})
return {
txHash: status.terminationTxHash === '' ? undefined : status.terminationTxHash,
...(status.confirmedTxHash === undefined ? {} : { confirmedTxHash: status.confirmedTxHash }),
dataSetId,
endEpoch: status.serviceTerminationEpoch,
}
Expand All @@ -102,6 +108,7 @@ export async function terminateServiceFlow(
const status = await waitForTerminateService({ statusUrl, onHash: onSubmitted })
return {
txHash: status.terminationTxHash === '' ? undefined : status.terminationTxHash,
...(status.confirmedTxHash === undefined ? {} : { confirmedTxHash: status.confirmedTxHash }),
dataSetId,
endEpoch: status.serviceTerminationEpoch,
}
Expand Down
41 changes: 37 additions & 4 deletions packages/synapse-sdk/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -619,16 +619,32 @@ export interface CommitOptions {
pieces: Array<{ pieceCid: PieceCID; pieceMetadata?: MetadataObject }>
/** Pre-built signed extraData (avoids re-signing) */
extraData?: Hex
/** Called when the commit transaction is submitted (before on-chain confirmation) */
/**
* Called when the commit transaction is submitted (before on-chain confirmation).
* The hash is Curio's Location wait key and is not guaranteed to be the final
* included on-chain hash if Curio replaces the send by fee. Prefer
* {@link CommitResult.confirmedTxHash} after confirmation.
*/
onSubmitted?: (txHash: Hex) => void
}

/**
* Result of a commit operation
*/
export interface CommitResult {
/** Transaction hash */
/**
* Original Location / wait-key transaction hash from Curio.
* Not necessarily the included on-chain hash after replace-by-fee.
* Keep using this for Curio status polling / resume URLs.
*/
txHash: Hex
/**
* Hash included on chain once confirmed. Differs from {@link txHash} when
* Curio replaced the original send by fee. Omitted when the SP does not
* report it (older Curio).
* For explorers and receipt lookups use `confirmedTxHash ?? txHash`.
*/
confirmedTxHash?: Hex
/** Piece IDs assigned by the contract */
pieceIds: bigint[]
/** Data set ID (may be newly created) */
Expand All @@ -649,16 +665,33 @@ export interface TerminateServiceOptions {
* cooperation, but the service runs to the end of the lockup period.
*/
skipProvider?: boolean
/** Called when the termination transaction is submitted (before on-chain confirmation) */
/**
* Called when the termination transaction is submitted (before on-chain confirmation).
* For provider-relayed termination this is Curio's wait-key hash and may differ
* from the included on-chain hash after replace-by-fee. Prefer
* {@link TerminateServiceResult.confirmedTxHash} after confirmation.
*/
onSubmitted?: (txHash: Hex) => void
}

/**
* Result of a data set service termination
*/
export interface TerminateServiceResult {
/** Transaction hash. Undefined when the service was already terminated without a provider transaction. */
/**
* Original wait-key transaction hash. Undefined when the service was already
* terminated without a provider transaction. May differ from the included
* on-chain hash after replace-by-fee.
* Keep using this for Curio status polling / resume URLs.
*/
txHash?: Hex
/**
* Hash included on chain once confirmed. Differs from {@link txHash} when
* Curio replaced the original send by fee. Omitted when the SP does not
* report it (older Curio) or when no termination tx was sent.
* For explorers and receipt lookups use `confirmedTxHash ?? txHash`.
*/
confirmedTxHash?: Hex
/** The data set ID */
dataSetId: bigint
/**
Expand Down