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
1 change: 0 additions & 1 deletion contracts/flashstack-stx-core.clar
Original file line number Diff line number Diff line change
Expand Up @@ -196,4 +196,3 @@
(define-read-only (get-admin)
(ok (var-get admin))
)

1 change: 0 additions & 1 deletion contracts/test/flashstack-stx-core.clar
Original file line number Diff line number Diff line change
Expand Up @@ -196,4 +196,3 @@
(define-read-only (get-admin)
(ok (var-get admin))
)

32 changes: 28 additions & 4 deletions tests/deployed-source-pins.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,28 +15,52 @@ import { readFileSync } from "node:fs";
* create drift from what is on chain. If a change is ever legitimate, it is a new
* contract under a new name, not an edit to these files.
*
* Offline on purpose. To re-verify against the chain:
* curl -s https://api.hiro.so/v2/contracts/source/SP3TGRVG7DKGFVRTTVGGS60S59R916FWB4DAB9STZ/<name> \
* ajv.2.4 (axis 2 — repo copy vs deployed mainnet source): the same mechanism now
* also pins flashstack-stx-core and flashstack-sbtc-core, the two P0 live cores
* reviewed under ajv.4.3, checked byte-for-byte against SP20XD46NGAX05ZQZDKFYCCX49A3852BQABNP0VG5
* on 2026-10-01. A live-fetch CI job was deliberately not built — it would make the
* merge gate depend on a third-party API and network access, which needs its own
* decision (blocking gate vs scheduled job). This offline pin catches the same
* drift — any edit to these files — for zero network cost; re-running the curl
* below against mainnet is still how the pin itself gets re-verified.
*
* Offline on purpose. To re-verify against the chain (principal varies by pin,
* see each entry above):
* curl -s https://api.hiro.so/v2/contracts/source/<principal>/<name> \
* | jq -j .source | shasum -a 256
* (-j: no trailing newline; the API returns the source without one.)
* *.clar is forced to LF by .gitattributes, so the hash is stable across checkouts.
*/
const PINS = [
{
file: "contracts/snp-flashstack-receiver.clar",
principal: "SP3TGRVG7DKGFVRTTVGGS60S59R916FWB4DAB9STZ",
bytes: 3270,
sha256: "0722955560dbd791d5c3a29c84e1787e4a250df88db5ece6d27be768a0b920ea",
},
{
file: "contracts/snp-flashstack-receiver-v3.clar",
principal: "SP3TGRVG7DKGFVRTTVGGS60S59R916FWB4DAB9STZ",
bytes: 5396,
sha256: "2560814d0e0103d2e8fcb337fc560cdbc5c39215828b81606712ab82ffb3fdec",
},
{
file: "contracts/flashstack-stx-core.clar",
principal: "SP20XD46NGAX05ZQZDKFYCCX49A3852BQABNP0VG5",
bytes: 6537,
sha256: "9fde1e3e330e16310d6aeae51baaa3acece0e4c6aad6368f7b80967cd36b517e",
},
{
file: "contracts/flashstack-sbtc-core.clar",
principal: "SP20XD46NGAX05ZQZDKFYCCX49A3852BQABNP0VG5",
bytes: 6801,
sha256: "f723d2bea72e62bece76d6f2ebe3d7d1a9de707176f41b7bc3b709e8e4f839af",
},
] as const;

describe("verbatim copies of deployed contracts must not change", () => {
for (const { file, bytes, sha256 } of PINS) {
it(`${file} still matches the deployed source it was verified against`, () => {
for (const { file, principal, bytes, sha256 } of PINS) {
it(`${file} still matches the source deployed at ${principal}`, () => {
const raw = readFileSync(file, "utf-8").replace(/\r\n/g, "\n");
expect(Buffer.byteLength(raw), "size changed").toBe(bytes);
expect(createHash("sha256").update(raw).digest("hex"), "content changed").toBe(sha256);
Expand Down
Loading