Skip to content
Closed
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
4 changes: 3 additions & 1 deletion apps/passport-client/components/shared/PCDCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,9 @@ export const TicketQRWrapper = forwardRef<
);
}
if (isPODTicketPCD(pcd)) {
const urls = getURLsBasedOnCategory(pcd.claim.ticket.ticketCategory);
const urls = getURLsBasedOnCategory(
pcd.claim.ticket.ticketCategory ?? TicketCategory.Generic
);
if (urls.idBasedVerifyURL)
return (
<QRContainer ref={ref}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,14 @@ export function csvRowToPODTicketData(
ticketId, // The ticket ID is a unique identifier of the ticket.
eventId, // The event ID uniquely identifies an event.
productId, // The product ID uniquely identifies the type of ticket (e.g. General Admission, Volunteer etc.).
timestampConsumed: 0, // change if checkin feature enabled for csv pipelines
timestampConsumed: undefined, // change if checkin feature enabled for csv pipelines
timestampSigned: Date.now(),
attendeeSemaphoreId,
ticketSecret: undefined,
owner,
isConsumed: false, // changes if checkin feature enabled for csv pipelines
isRevoked: false,
ticketCategory: TicketCategory.Generic,
isConsumed: false, // change if checkin feature enabled for csv pipelines
isRevoked: undefined, // change if revocation feature changes to not just delete tickets
ticketCategory: undefined, // change if we ever support multiple categories for csv pipelines
attendeeName,
attendeeEmail
} satisfies IPODTicketData;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -364,11 +364,11 @@ export class CSVTicketPipeline implements BasePipeline {
...atom,
owner: semaphoreV4Id,
eventName: this.definition.options.eventName,
ticketCategory: TicketCategory.Generic,
timestampConsumed: 0,
//ticketCategory: TicketCategory.Generic,
//timestampConsumed: 0,
timestampSigned: new Date().getTime(),
isConsumed: false,
isRevoked: false
isConsumed: false
//isRevoked: false
};

const pcd = await PODTicketPCDPackage.prove({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1018,6 +1018,9 @@ export class PretixPipeline implements BasePipeline {
manualTicket.id
);

// Commented-out fields below are optional, unneded for current use cases,
// and omitted to keep the ticket POD below 16 entries (Merkle depth 5
// in GPC proofs).
return {
ticketId: manualTicket.id,
eventId: manualTicket.eventId,
Expand All @@ -1030,10 +1033,10 @@ export class PretixPipeline implements BasePipeline {
eventLocation: event.imageOptions?.eventLocation,
isAddOn: product.isAddOnItem,
isConsumed: checkIn ? true : false,
isRevoked: false,
// isRevoked: false,
timestampSigned: Date.now(),
timestampConsumed: checkIn ? checkIn.timestamp.getTime() : 0,
ticketCategory: TicketCategory.Generic,
// timestampConsumed: checkIn ? checkIn.timestamp.getTime() : 0,
// ticketCategory: TicketCategory.Generic,
eventName: event.name,
ticketName: product.name,
checkerEmail: undefined,
Expand Down Expand Up @@ -1273,17 +1276,20 @@ export class PretixPipeline implements BasePipeline {
throw new Error(`Atom missing email: ${atom.id} in pipeline ${this.id}`);
}

// Commented-out fields below are optional, unneded for current use cases,
// and omitted to keep the ticket POD below 16 entries (Merkle depth 5
// in GPC proofs).
return {
attendeeName: atom.name,
attendeeEmail: atom.email,
eventName: this.atomToEventName(atom),
ticketName: this.atomToTicketName(atom),
checkerEmail: undefined,
//checkerEmail: undefined,
ticketSecret: atom.secret,
ticketId: atom.id,
eventId: atom.eventId,
productId: atom.productId,
timestampConsumed: atom.timestampConsumed?.getTime() ?? 0,
//timestampConsumed: atom.timestampConsumed?.getTime() ?? 0,
timestampSigned: Date.now(),
owner: semaphoreV4Id,
imageUrl: this.atomToImageUrl(atom),
Expand All @@ -1292,8 +1298,8 @@ export class PretixPipeline implements BasePipeline {
eventLocation: this.atomToEventLocation(atom),
isAddOn: !!atom.parentAtomId,
isConsumed: atom.isConsumed,
isRevoked: false,
ticketCategory: TicketCategory.Generic,
//isRevoked: false,
//ticketCategory: TicketCategory.Generic,
parentTicketId: atom.parentAtomId ?? undefined
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -420,9 +420,8 @@ describe("generic issuance - PretixPipeline with semaphore v4 enabled", function
expect(ManualAttendeePODTicket.claim.ticket.imageUrl).to.eq(
EthLatAmImageUrl
);
expect(ManualAttendeePODTicket.claim.ticket.timestampConsumed).to.eq(
Date.now()
);
expect(ManualAttendeePODTicket.claim.ticket.timestampConsumed).to.be
.undefined;
}

const manualBouncerChecksInManualAttendeeAgain =
Expand Down
9 changes: 3 additions & 6 deletions examples/test-zapp/src/apis/GPC.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -201,13 +201,10 @@ const ticketData = {
ticketName: "Ticket 1",
eventName: "Event 1",
ticketSecret: "secret123",
timestampConsumed: 1714857600,
timestampSigned: 1714857600,
attendeeSemaphoreId: ${identityV3},
owner: "${publicKey}",
isConsumed: 0,
isRevoked: 0,
ticketCategory: 0,
attendeeName: "John Doe",
attendeeEmail: "test@example.com"
};
Expand All @@ -227,13 +224,13 @@ await z.pod.insert(pod);
ticketName: "Ticket 1",
eventName: "Event 1",
ticketSecret: "secret123",
timestampConsumed: 1714857600,
//timestampConsumed: 1714857600,
timestampSigned: 1714857600,
attendeeSemaphoreId: identityV3 as bigint,
owner: publicKey as string,
isConsumed: 0,
isRevoked: 0,
ticketCategory: 0,
//isRevoked: 0,
//ticketCategory: 0,
attendeeName: "John Doe",
attendeeEmail: "test@example.com"
};
Expand Down
6 changes: 3 additions & 3 deletions packages/pcd/pod-ticket-pcd/src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const TicketDataSchema = z.object({
ticketId: z.string().uuid(),
eventId: z.string().uuid(),
productId: z.string().uuid(),
timestampConsumed: z.number().int().nonnegative(),
timestampConsumed: z.number().int().nonnegative().optional(),
timestampSigned: z.number().int().nonnegative(),
/**
* V3 semaphore commitment.
Expand All @@ -35,8 +35,8 @@ export const TicketDataSchema = z.object({
// `dataToPodEntries` will not work
.transform(eddsaPublicKey),
isConsumed: z.boolean(),
isRevoked: z.boolean(),
ticketCategory: z.nativeEnum(TicketCategory),
isRevoked: z.boolean().optional(),
ticketCategory: z.nativeEnum(TicketCategory).optional(),
attendeeName: z.string(),
attendeeEmail: z.string(),
qrCodeOverrideImageUrl: z.string().optional(),
Expand Down
2 changes: 1 addition & 1 deletion packages/pcd/pod-ticket-pcd/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export function dataToPodEntries<T>(
// wraps a String is either a String or missing entirely.
if (typeName === "ZodOptional") {
// If there's no value for this field, don't add an entry for it.
if (!data[key]) {
if (data[key] === undefined) {
continue;
} else {
typeName = field._def.innerType._def.typeName;
Expand Down
44 changes: 44 additions & 0 deletions packages/pcd/pod-ticket-pcd/test/pod-ticket-pcd.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,50 @@ describe("PODTicketPCD should work", function () {
expect(ticketPOD.signerPublicKey).to.eq(expectedPublicKey);
});

it("should be able to create and verify a ticket with minimal entries", async function () {
const noOptionalFieldsTicketData: IPODTicketData = {
attendeeName: "test name",
attendeeEmail: "user@test.com",
eventName: "event",
ticketName: "ticket",
checkerEmail: "checker@test.com",
ticketId: "0450fd86-fa6f-430b-81ac-24b03a75be01",
eventId: "d451327c-9997-449a-a6fb-bea11e816533",
productId: "a7c633a4-618a-474c-bb33-523ba68e6314",
timestampSigned: Date.UTC(2024, 10, 11),
// Owner is optional, but implicitly one of the two types of
// ownership should be present.
owner: "9x0qSqXus/VG4OgfyHWvVEFIiaTa7rsE/kS0YsHNNQI",
isConsumed: false
};

const noOptionalFieldsTicket = await PODTicketPCDPackage.prove({
ticket: {
value: noOptionalFieldsTicketData,
argumentType: ArgumentTypeName.Object
},
privateKey: {
value: prvKey,
argumentType: ArgumentTypeName.String
},
id: {
value: COMPAT_TEST_PCD_ID,
argumentType: ArgumentTypeName.String
}
});

expect(await PODTicketPCDPackage.verify(noOptionalFieldsTicket)).to.be.true;
expect(noOptionalFieldsTicket.type).to.eq(PODTicketPCDTypeName);
expect(noOptionalFieldsTicket.id).to.eq(COMPAT_TEST_PCD_ID);

const noOptionalFieldsTicketPOD = ticketToPOD(noOptionalFieldsTicket);
expect(noOptionalFieldsTicketPOD.verifySignature()).to.be.true;
console.error("ART_DBG", noOptionalFieldsTicketPOD.content.asEntries());
expect(
Object.keys(noOptionalFieldsTicketPOD.content.asEntries())
).to.have.length(12);
});

it("should not be possible to verify a ticket that has been tampered with", async function () {
const originalTicketData = ticket.claim.ticket;
ticket.claim.ticket = {
Expand Down