Skip to content

Construct a TaskSpec and Bid

This quickstart uses public release artifacts to reproduce a TaskSpec commitment and verify the exact OCH-1.1 Bid signing shape. The Bid fixture is cryptographic test data, not a live task or live hub transaction.

Install Node.js 22 or 24, Foundry for cast, and jq.

Download the worked TaskSpec vector and the standalone canonicalizer:

Terminal window
NORTHSET_ARTIFACT_BASE_URL=${NORTHSET_ARTIFACT_BASE_URL:-https://docs.northset.ai/artifacts}
curl -fsS "$NORTHSET_ARTIFACT_BASE_URL/vectors/task-spec-v1-1.json" \
-o /tmp/northset-task-spec-vector.json
curl -fsS "$NORTHSET_ARTIFACT_BASE_URL/examples/canonicalize-task-spec.mjs" \
-o /tmp/canonicalize-task-spec.mjs
node /tmp/canonicalize-task-spec.mjs \
/tmp/northset-task-spec-vector.json \
> /tmp/northset-task-spec.canonical.json
SPEC_HASH=$(cast keccak "$(cat /tmp/northset-task-spec.canonical.json)")
EXPECTED_HASH=$(jq -r '.specHash' /tmp/northset-task-spec-vector.json)
test "$SPEC_HASH" = "$EXPECTED_HASH" && echo "TaskSpec vector: PASS"

Expected result:

TaskSpec vector: PASS

The expected commitment is:

0xeac791794ef8b9f544ba597f104393d91db65a7d8499130d0bd4d0c038546ce0

The canonicalizer implements the OCH-1.1 rule keccak256(RFC8785_JCS(taskSpecJson)). TaskSpec numbers must be integers no greater than 2^53 - 1; represent larger exact values as strings.

Change one committed field, canonicalize again, and confirm that the commitment differs:

Terminal window
jq '.taskSpec.deadlines.maxComputeWindowSec = 3601 | .taskSpec' \
/tmp/northset-task-spec-vector.json \
> /tmp/northset-task-spec-tampered.json
node /tmp/canonicalize-task-spec.mjs \
/tmp/northset-task-spec-tampered.json \
> /tmp/northset-task-spec-tampered.canonical.json
TAMPERED_HASH=$(cast keccak "$(cat /tmp/northset-task-spec-tampered.canonical.json)")
EXPECTED_TAMPERED_HASH=$(jq -r '.tamper.actualSpecHash' /tmp/northset-task-spec-vector.json)
if test "$TAMPERED_HASH" = "$EXPECTED_TAMPERED_HASH" \
&& test "$TAMPERED_HASH" != "$EXPECTED_HASH"; then
echo "TaskSpec tamper: DETECTED"
else
echo "TaskSpec tamper: FAILED" >&2
false
fi

Expected result:

TaskSpec tamper: DETECTED

Download the signed fixture and extract the EIP-712 object accepted by cast:

Terminal window
curl -fsS "$NORTHSET_ARTIFACT_BASE_URL/vectors/bid-v1-1.json" \
-o /tmp/northset-bid-vector.json
jq '.envelope | {types, primaryType, domain, message}' \
/tmp/northset-bid-vector.json \
> /tmp/northset-bid-typed-data.json
SIGNER=$(jq -r '.envelope.signer' /tmp/northset-bid-vector.json)
SIGNATURE=$(jq -r '.envelope.signature' /tmp/northset-bid-vector.json)
cast wallet verify --data --from-file \
--address "$SIGNER" \
/tmp/northset-bid-typed-data.json \
"$SIGNATURE"

Expected result:

Validation succeeded. Address 0xe05fcC23807536bEe418f142D19fa0d21BB0cfF7 signed this message.

The fixture binds the domain name OpenclawHub, version 1.1, chain ID, verifying contract, and all ten Bid fields. For a live bid, read chainId from the wallet or provider, set verifyingContract to the deployed hub, and read the current nonce from bidNonce(delegatee).

Change the signed reward and confirm that the original signature no longer validates for the documented signer:

Terminal window
jq '. as $vector
| .envelope
| {types, primaryType, domain, message}
| .message.rewardUSDC = $vector.tamper.replacement' \
/tmp/northset-bid-vector.json \
> /tmp/northset-bid-tampered.json
if BID_TAMPER_OUTPUT=$(cast wallet verify --data --from-file \
--address "$SIGNER" \
/tmp/northset-bid-tampered.json \
"$SIGNATURE" 2>&1); then
echo "Bid tamper: FAILED" >&2
false
elif printf '%s\n' "$BID_TAMPER_OUTPUT" | grep -q "Validation failed"; then
echo "Bid tamper: DETECTED"
else
echo "Bid tamper: FAILED" >&2
printf '%s\n' "$BID_TAMPER_OUTPUT" >&2
false
fi

Expected result:

Bid tamper: DETECTED

The published STATS_V1 vectors include two positive cases and one deliberately changed input. Recompute the changed input commitment and compare it with the original commitment:

Terminal window
curl -fsS "$NORTHSET_ARTIFACT_BASE_URL/vectors/stats-v1.json" \
-o /tmp/northset-stats-v1.json
VALUES=$(jq -c '.tamper.values' /tmp/northset-stats-v1.json)
EXPECTED_INPUT_HASH=$(jq -r '.tamper.expectedInputHash' /tmp/northset-stats-v1.json)
ACTUAL_INPUT_HASH=$(cast abi-encode 'f(uint64[])' "$VALUES" | cast keccak)
if test "$ACTUAL_INPUT_HASH" != "$EXPECTED_INPUT_HASH"; then
echo "STATS input tamper: DETECTED"
else
echo "STATS input tamper: FAILED" >&2
false
fi

Expected result:

STATS input tamper: DETECTED

For STATS_V1 execution today, arrange the Northset-coordinated proof path through integration support and complete a task-bound dry run before signing or activating a bid. The public artifacts cover commitments, typed-data signing, contract interfaces, and receipt verification without exposing private proof-generation implementation.

Continue with integration artifacts or verify Task 22 settlement.