Skip to content
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
26 changes: 16 additions & 10 deletions all_networks_shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import {
HEARTBEAT_RELAY_EVM_PRIVATE_KEY_ENV,
HEARTBEAT_RELAY_REGISTRY_CONTRACT_ENV,
REDIS_URL,
teeSignatureLeafValue,
toBytesCalldata,
toStrictBytes32,
type DataSettlementJobData,
Expand Down Expand Up @@ -262,12 +261,20 @@ async function flushBatchSettlementBuffer(

try {
const values = items.map(item => [
toStrictBytes32(item.teeId, "teeId"),
toStrictBytes32(item.inputHash, "inputHash"),
toStrictBytes32(item.outputHash, "outputHash"),
teeSignatureLeafValue(item.teeSignature),
base64ToBytesCalldata(item.teeSignature),
item.timestamp,
]);

const tree = StandardMerkleTree.of(values, ["bytes32", "bytes32", "bytes32"]);
const tree = StandardMerkleTree.of(values, [
"bytes32",
"bytes32",
"bytes32",
"bytes",
"uint256",
]);
const merkleRoot = tree.root;
const treeData = JSON.stringify(tree.dump());
const blobId = await uploadToWalrus(treeData, "batch-tree");
Expand Down Expand Up @@ -375,7 +382,6 @@ export async function uploadToWalrus(
body: data,
headers: {
"Content-Type": "application/json",

},
signal: abortController.signal,
});
Expand Down Expand Up @@ -572,7 +578,11 @@ export function createDataWorkerContext(): DataWorkerContext {
}
return txHash;
} catch (error) {
incrementMetric("data.tx.failure.count", ["worker:data", "tx_type:batch", `stage:${stage}`]);
incrementMetric("data.tx.failure.count", [
"worker:data",
"tx_type:batch",
`stage:${stage}`,
]);
throw error;
}
},
Expand Down Expand Up @@ -656,11 +666,7 @@ export function createHeartbeatRelayContext(): HeartbeatRelayContext {
chainId: ogEvm.id,
chainName: ogEvm.name,
registryContractAddress,
submitHeartbeat: async ({
teeId,
timestamp,
signature,
}): Promise<`0x${string}`> => {
submitHeartbeat: async ({ teeId, timestamp, signature }): Promise<`0x${string}`> => {
let stage: "broadcast" | "receipt" = "broadcast";
let txHash: `0x${string}` | undefined;
try {
Expand Down
66 changes: 29 additions & 37 deletions all_networks_types_helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const PAYMENT_QUEUE_NAME = resolveQueueName(
);
export const DATA_SETTLEMENT_QUEUE_NAME = resolveQueueName(
process.env.DATA_SETTLEMENT_QUEUE_NAME,
"x402-settle-data-queue",
"x402-settle-data-queue-v2",
"DATA_SETTLEMENT_QUEUE_NAME",
);
export const SHUTDOWN_TIMEOUT_MS = Number(process.env.SHUTDOWN_TIMEOUT_MS || 10_000);
Expand All @@ -51,9 +51,11 @@ export const DATA_SETTLEMENT_BATCH_MAX_AGE_MS = Number(
export type SettlementType = "private" | "batch" | "individual";

export type SettlementBatchData = {
teeId: `0x${string}`;
inputHash: string;
outputHash: string;
teeSignature: string;
timestamp: string;
};

export type SettlementIndividualData = SettlementBatchData & {
Expand Down Expand Up @@ -143,7 +145,10 @@ function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null;
}

export function getRequiredStringField(record: Record<string, unknown>, fieldNames: string[]): string {
export function getRequiredStringField(
record: Record<string, unknown>,
fieldNames: string[],
): string {
for (const name of fieldNames) {
const value = record[name];
if (typeof value === "string" && value.length > 0) {
Expand All @@ -153,7 +158,10 @@ export function getRequiredStringField(record: Record<string, unknown>, fieldNam
throw new Error(`Missing required settlement field. Expected one of: ${fieldNames.join(", ")}`);
}

export function getRequiredUnknownField(record: Record<string, unknown>, fieldNames: string[]): unknown {
export function getRequiredUnknownField(
record: Record<string, unknown>,
fieldNames: string[],
): unknown {
for (const name of fieldNames) {
if (name in record) {
return record[name];
Expand All @@ -167,13 +175,17 @@ export function parseUint256Field(record: Record<string, unknown>, fieldNames: s

if (typeof raw === "number") {
if (!Number.isInteger(raw) || raw < 0) {
throw new Error(`Invalid uint256 field. Expected non-negative integer for: ${fieldNames.join(", ")}`);
throw new Error(
`Invalid uint256 field. Expected non-negative integer for: ${fieldNames.join(", ")}`,
);
}
return raw.toString();
}

if (typeof raw !== "string") {
throw new Error(`Invalid uint256 field. Expected string or number for: ${fieldNames.join(", ")}`);
throw new Error(
`Invalid uint256 field. Expected string or number for: ${fieldNames.join(", ")}`,
);
}

const trimmed = raw.trim();
Expand All @@ -196,7 +208,10 @@ export function parseUint256Field(record: Record<string, unknown>, fieldNames: s
return trimmed;
}

function parseEvmAddressField(record: Record<string, unknown>, fieldNames: string[]): `0x${string}` {
function parseEvmAddressField(
record: Record<string, unknown>,
fieldNames: string[],
): `0x${string}` {
const value = getRequiredStringField(record, fieldNames);
if (!isAddress(value)) {
throw new Error(`Invalid EVM address for: ${fieldNames.join(", ")}`);
Expand All @@ -210,20 +225,11 @@ function parseBatchSettlementData(decoded: unknown): SettlementBatchData {
}

return {
inputHash: getRequiredStringField(decoded, ["inputHash", "input_hash", "input hash", "input-hash"]),
outputHash: getRequiredStringField(decoded, [
"outputHash",
"output_hash",
"output hash",
"output-hash",
]),
teeSignature: getRequiredStringField(decoded, [
"teeSignature",
"tee_signature",
"tee signature",
"tee-signature",
"tee singature",
]),
teeId: toStrictBytes32(getRequiredStringField(decoded, ["tee_id"]), "teeId"),
inputHash: getRequiredStringField(decoded, ["input_hash"]),
outputHash: getRequiredStringField(decoded, ["output_hash"]),
teeSignature: getRequiredStringField(decoded, ["tee_signature"]),
timestamp: parseUint256Field(decoded, ["tee_timestamp"]),
};
}

Expand All @@ -233,23 +239,9 @@ function parseIndividualSettlementData(decoded: unknown): SettlementIndividualDa
}

const batchData = parseBatchSettlementData(decoded);
const teeId = toStrictBytes32(
getRequiredStringField(decoded, ["teeId", "tee_id", "tee id", "tee-id"]),
"teeId",
);
const timestamp = parseUint256Field(decoded, [
"timestamp",
"timeStamp",
"time_stamp",
"tee_timestamp",
]);
const ethAddress = parseEvmAddressField(decoded, [
"ethAddress",
"eth_address",
"eth address",
"eth-address",
"address",
]);
const teeId = toStrictBytes32(getRequiredStringField(decoded, ["tee_id"]), "teeId");
const timestamp = parseUint256Field(decoded, ["timestamp"]);
const ethAddress = parseEvmAddressField(decoded, ["eth_address"]);

return {
...batchData,
Expand Down
Loading