Skip to content

Commit

Permalink
Fix proof message timestamp validation & add builders for UsernamePro…
Browse files Browse the repository at this point in the history
…ofData and UsernameProofMessage (#1164)
  • Loading branch information
cmlad authored Jul 19, 2023
1 parent 998979d commit e36fcae
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 9 deletions.
5 changes: 5 additions & 0 deletions .changeset/early-trains-shake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@farcaster/core": patch
---

Fix username proof timestamp validation + add builders for UsernameProofData and UsernameProofMessage
8 changes: 5 additions & 3 deletions apps/hubble/src/storage/engine/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
RevokeMessageHubEvent,
SignerAddMessage,
SignerRemoveMessage,
toFarcasterTime,
UserDataAddMessage,
UserDataType,
UserNameProof,
Expand Down Expand Up @@ -476,7 +477,8 @@ describe("mergeMessage", () => {
});

const createProof = async (name: string, timestamp?: number | null, owner?: string | null) => {
const ts = timestamp ?? getFarcasterTime()._unsafeUnwrap();
// Proof time is in Unix seconds and should match the message time which is in Farcaster milliseconds
const timestampSec = Math.floor((timestamp || Date.now()) / 1000);
const ownerAsBytes = owner ? hexStringToBytes(owner)._unsafeUnwrap() : Factories.EthAddress.build();
return await Factories.UsernameProofMessage.create(
{
Expand All @@ -486,10 +488,10 @@ describe("mergeMessage", () => {
name: utf8StringToBytes(name)._unsafeUnwrap(),
fid,
owner: ownerAsBytes,
timestamp: fromFarcasterTime(ts)._unsafeUnwrap(),
timestamp: timestampSec,
type: UserNameType.USERNAME_TYPE_ENS_L1,
}),
timestamp: ts,
timestamp: toFarcasterTime(timestampSec * 1000)._unsafeUnwrap(),
type: MessageType.USERNAME_PROOF,
},
},
Expand Down
52 changes: 50 additions & 2 deletions packages/core/src/builders.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import { faker } from "@faker-js/faker";
import * as protobufs from "./protobufs";
import { err, ok } from "neverthrow";
import * as builders from "./builders";
import { hexStringToBytes } from "./bytes";
import { bytesToHexString, hexStringToBytes, utf8StringToBytes } from "./bytes";
import { HubError } from "./errors";
import { Factories } from "./factories";
import * as validations from "./validations";
import { VerificationEthAddressClaim, makeVerificationEthAddressClaim } from "./verifications";
import { getFarcasterTime } from "./time";
import { getFarcasterTime, toFarcasterTime } from "./time";
import { makeUserNameProofClaim } from "./userNameProof";

const fid = Factories.Fid.build();
const network = protobufs.FarcasterNetwork.TESTNET;
Expand Down Expand Up @@ -389,3 +390,50 @@ describe("makeLinkRemove", () => {
expect(isValid.isOk()).toBeTruthy();
});
});

describe("username proof", () => {
const fid = Factories.Fid.build();
const proofTimestamp = Math.floor(Date.now() / 1000);
const messageTimestamp = toFarcasterTime(proofTimestamp * 1000)._unsafeUnwrap();
const name = "test.eth";

let proof: protobufs.UserNameProof;

beforeAll(async () => {
const claim = makeUserNameProofClaim({
name,
owner: bytesToHexString(ethSignerKey)._unsafeUnwrap(),
timestamp: proofTimestamp,
});
const signature = (await eip712Signer.signUserNameProofClaim(claim))._unsafeUnwrap();
expect(signature).toBeTruthy();
proof = {
timestamp: proofTimestamp,
name: utf8StringToBytes(name)._unsafeUnwrap(),
owner: ethSignerKey,
signature,
fid,
type: protobufs.UserNameType.USERNAME_TYPE_ENS_L1,
};
});

describe("makeUsernameProofData", () => {
test("succeeds", async () => {
const data = await builders.makeUsernameProofData(proof, { fid, network, timestamp: messageTimestamp });
const isValid = await validations.validateMessageData(data._unsafeUnwrap());
expect(isValid.isOk()).toBeTruthy();
});
});

describe("makeUsernameProof", () => {
test("succeeds", async () => {
const message = await builders.makeUsernameProof(
proof,
{ fid, network, timestamp: messageTimestamp },
ed25519Signer,
);
const isValid = await validations.validateMessage(message._unsafeUnwrap());
expect(isValid.isOk()).toBeTruthy();
});
});
});
20 changes: 20 additions & 0 deletions packages/core/src/builders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type MessageBodyOptions = Pick<
| "signerRemoveBody"
| "userDataBody"
| "linkBody"
| "usernameProofBody"
>;

/** Generic Methods */
Expand Down Expand Up @@ -334,3 +335,22 @@ export const makeUserDataAddData = (
): HubAsyncResult<protobufs.UserDataAddData> => {
return makeMessageData({ userDataBody: body }, protobufs.MessageType.USER_DATA_ADD, dataOptions);
};

export const makeUsernameProof = async (
body: protobufs.UserNameProof,
dataOptions: MessageDataOptions,
signer: Signer,
): HubAsyncResult<protobufs.UsernameProofMessage> => {
const data = await makeUsernameProofData(body, dataOptions);
if (data.isErr()) {
return err(data.error);
}
return makeMessage(data.value, signer);
};

export const makeUsernameProofData = (
body: protobufs.UserNameProof,
dataOptions: MessageDataOptions,
): HubAsyncResult<protobufs.UsernameProofData> => {
return makeMessageData({ usernameProofBody: body }, protobufs.MessageType.USERNAME_PROOF, dataOptions);
};
5 changes: 3 additions & 2 deletions packages/core/src/factories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -560,7 +560,8 @@ const UsernameProofDataFactory = Factory.define<protobufs.UsernameProofData>(()
return MessageDataFactory.build({
usernameProofBody: proofBody,
type: protobufs.MessageType.USERNAME_PROOF,
timestamp: toFarcasterTime(proofBody.timestamp)._unsafeUnwrap(),
// Proof timestamp is in Unix seconds
timestamp: toFarcasterTime(proofBody.timestamp * 1000)._unsafeUnwrap(),
fid: proofBody.fid,
}) as protobufs.UsernameProofData;
});
Expand Down Expand Up @@ -657,7 +658,7 @@ const StorageAdminRegistryEventFactory = Factory.define<protobufs.StorageAdminRe

const UserNameProofFactory = Factory.define<protobufs.UserNameProof>(() => {
return protobufs.UserNameProof.create({
timestamp: Date.now(),
timestamp: Math.floor(Date.now() / 1000),
signature: Eip712SignatureFactory.build(),
owner: EthAddressFactory.build(),
name: FnameFactory.build(),
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/signers/testUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export const testEip712Signer = async (signer: Eip712Signer) => {
beforeAll(async () => {
claim = makeUserNameProofClaim({
name: "0x000",
timestamp: Date.now(),
timestamp: Math.floor(Date.now() / 1000),
owner: bytesToHex(signerKey),
});
const signatureResult = await signer.signUserNameProofClaim(claim);
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/validations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,8 @@ export const validateUsernameProofBody = (
);
}

const proofFarcasterTimestamp = toFarcasterTime(body.timestamp);
// Proof time is in Unix seconds
const proofFarcasterTimestamp = toFarcasterTime(body.timestamp * 1000);
if (proofFarcasterTimestamp.isErr()) {
return err(proofFarcasterTimestamp.error);
}
Expand Down

0 comments on commit e36fcae

Please sign in to comment.