From 2001fa64fe3c1066b760b8389929445afdcdae72 Mon Sep 17 00:00:00 2001 From: Saleel Date: Wed, 3 Apr 2024 20:00:49 +0530 Subject: [PATCH] circuit: fix tests to match new helper methods --- packages/circuits/email-verifier.circom | 16 +-- .../circuits/tests/email-verifier.test.ts | 134 +++++------------- packages/circuits/tests/rsa.test.ts | 29 ++-- packages/circuits/tests/sha.test.ts | 4 +- packages/helpers/src/input-generators.ts | 4 +- .../tests/test-data/email-good-large.eml | 104 ++++++++++++++ 6 files changed, 156 insertions(+), 135 deletions(-) create mode 100644 packages/helpers/tests/test-data/email-good-large.eml diff --git a/packages/circuits/email-verifier.circom b/packages/circuits/email-verifier.circom index f6539a6b5..1b6e5ce37 100644 --- a/packages/circuits/email-verifier.circom +++ b/packages/circuits/email-verifier.circom @@ -15,7 +15,7 @@ include "./utils/hash.circom"; /// @notice Circuit to verify email signature as per DKIM standard. /// @notice Verifies the signature is valid for the given header and pubkey, and the hash of the body matches the hash in the header. /// @notice This cicuit only verifies signature as per `rsa-sha256` algorithm. -/// @param maxHeaderLength Maximum length for the email header. +/// @param maxHeadersLength Maximum length for the email header. /// @param maxBodyLength Maximum length for the email body. /// @param n Number of bits per chunk the RSA key is split into. Recommended to be 121. /// @param k Number of chunks the RSA key is split into. Recommended to be 17. @@ -29,14 +29,14 @@ include "./utils/hash.circom"; /// @input bodyHashIndex Index of the body hash `bh` in the emailHeader. /// @input precomputedSHA Precomputed SHA-256 hash of the email body till the bodyHashIndex. /// @output pubkeyHash Poseidon hash of the pubkey - Poseidon(n/2)(n/2 chunks of pubkey with k*2 bits per chunk). -template EmailVerifier(maxHeaderLength, maxBodyLength, n, k, ignoreBodyHashCheck) { - assert(maxHeaderLength % 64 == 0); +template EmailVerifier(maxHeadersLength, maxBodyLength, n, k, ignoreBodyHashCheck) { + assert(maxHeadersLength % 64 == 0); assert(maxBodyLength % 64 == 0); assert(n * k > 2048); // to support 2048 bit RSA assert(n < (255 \ 2)); // for multiplication to fit in the field (255 bits) - signal input emailHeader[maxHeaderLength]; + signal input emailHeader[maxHeadersLength]; signal input emailHeaderLength; signal input pubkey[k]; signal input signature[k]; @@ -45,11 +45,11 @@ template EmailVerifier(maxHeaderLength, maxBodyLength, n, k, ignoreBodyHashCheck // Assert emailHeader data after emailHeaderLength are zeros - AssertZeroPadding(maxHeaderLength)(emailHeader, emailHeaderLength + 1); + AssertZeroPadding(maxHeadersLength)(emailHeader, emailHeaderLength + 1); // Calculate SHA256 hash of the `emailHeader` - 506,670 constraints - signal output sha[256] <== Sha256Bytes(maxHeaderLength)(emailHeader, emailHeaderLength); + signal output sha[256] <== Sha256Bytes(maxHeadersLength)(emailHeader, emailHeaderLength); // Pack SHA output bytes to int[] for RSA input message @@ -89,11 +89,11 @@ template EmailVerifier(maxHeaderLength, maxBodyLength, n, k, ignoreBodyHashCheck // Body hash regex - 617,597 constraints // Extract the body hash from the header (i.e. the part after bh= within the DKIM-signature section) - signal (bhRegexMatch, bhReveal[maxHeaderLength]) <== BodyHashRegex(maxHeaderLength)(emailHeader); + signal (bhRegexMatch, bhReveal[maxHeadersLength]) <== BodyHashRegex(maxHeadersLength)(emailHeader); bhRegexMatch === 1; var shaB64Length = 44; // Length of SHA-256 hash when base64 encoded - ceil(32 / 3) * 4 - signal bhBase64[shaB64Length] <== SelectRegexReveal(maxHeaderLength, shaB64Length)(bhReveal, bodyHashIndex); + signal bhBase64[shaB64Length] <== SelectRegexReveal(maxHeadersLength, shaB64Length)(bhReveal, bodyHashIndex); signal headerBodyHash[32] <== Base64Decode(32)(bhBase64); diff --git a/packages/circuits/tests/email-verifier.test.ts b/packages/circuits/tests/email-verifier.test.ts index 7ef5c1273..6aad3ccb7 100644 --- a/packages/circuits/tests/email-verifier.test.ts +++ b/packages/circuits/tests/email-verifier.test.ts @@ -3,9 +3,9 @@ import { buildPoseidon } from "circomlibjs"; import { wasm as wasm_tester } from "circom_tester"; import path from "path"; import { DKIMVerificationResult } from "@zk-email/helpers/src/dkim"; -import { generateCircuitInputs } from "@zk-email/helpers/src/input-helpers"; +import { generateEmailVerifierInputsFromDKIMResult } from "@zk-email/helpers/src/input-generators"; import { verifyDKIMSignature } from "@zk-email/helpers/src/dkim"; -import { bigIntToChunkedBytes } from "@zk-email/helpers/src/binaryFormat"; +import { bigIntToChunkedBytes } from "@zk-email/helpers/src/binary-format"; describe("EmailVerifier", () => { @@ -32,13 +32,8 @@ describe("EmailVerifier", () => { }); it("should verify email without any SHA precompute selector", async function () { - const emailVerifierInputs = generateCircuitInputs({ - rsaSignature: dkimResult.signature, - rsaPublicKey: dkimResult.publicKey, - body: dkimResult.body, - bodyHash: dkimResult.bodyHash, - message: dkimResult.message, - maxMessageLength: 640, + const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult(dkimResult, { + maxHeadersLength: 640, maxBodyLength: 768, }); @@ -47,14 +42,9 @@ describe("EmailVerifier", () => { }); it("should verify email with a SHA precompute selector", async function () { - const emailVerifierInputs = generateCircuitInputs({ - rsaSignature: dkimResult.signature, - rsaPublicKey: dkimResult.publicKey, - body: dkimResult.body, - bodyHash: dkimResult.bodyHash, - message: dkimResult.message, + const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult(dkimResult, { shaPrecomputeSelector: "How are", - maxMessageLength: 640, + maxHeadersLength: 640, maxBodyLength: 768, }); @@ -64,15 +54,10 @@ describe("EmailVerifier", () => { it("should fail if the rsa signature is wrong", async function () { const invalidRSASignature = dkimResult.signature + 1n; + const dkim = { ...dkimResult, signature: invalidRSASignature } - const emailVerifierInputs = generateCircuitInputs({ - rsaSignature: invalidRSASignature, - rsaPublicKey: dkimResult.publicKey, - body: dkimResult.body, - bodyHash: dkimResult.bodyHash, - message: dkimResult.message, - shaPrecomputeSelector: "How are", - maxMessageLength: 640, + const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult(dkim, { + maxHeadersLength: 640, maxBodyLength: 768, }); @@ -85,39 +70,14 @@ describe("EmailVerifier", () => { } }); - it("should fail if precompute string is not found in body", async function () { - const emailVerifierInputs = generateCircuitInputs({ - rsaSignature: dkimResult.signature, - rsaPublicKey: dkimResult.publicKey, - body: dkimResult.body, - bodyHash: dkimResult.bodyHash, - message: dkimResult.message, - shaPrecomputeSelector: "invalid", - maxMessageLength: 640, - maxBodyLength: 768, - }); + it("should fail if message is tampered", async function () { + const invalidHeader = Buffer.from(dkimResult.headers); + invalidHeader[0] = 1; - expect.assertions(1); - try { - const witness = await circuit.calculateWitness(emailVerifierInputs); - await circuit.checkConstraints(witness); - } catch (error) { - expect((error as Error).message).toMatch("Assert Failed"); - } - }); + const dkim = { ...dkimResult, headers: invalidHeader } - it("should fail if message is tampered", async function () { - const invalidMessage = Buffer.from(dkimResult.message); - invalidMessage[0] = 1; - - const emailVerifierInputs = generateCircuitInputs({ - rsaSignature: dkimResult.signature, - rsaPublicKey: dkimResult.publicKey, - body: dkimResult.body, - bodyHash: dkimResult.bodyHash, - message: invalidMessage, - shaPrecomputeSelector: "How are", - maxMessageLength: 640, + const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult(dkim, { + maxHeadersLength: 640, maxBodyLength: 768, }); @@ -131,14 +91,8 @@ describe("EmailVerifier", () => { }); it("should fail if message padding is tampered", async function () { - const emailVerifierInputs = generateCircuitInputs({ - rsaSignature: dkimResult.signature, - rsaPublicKey: dkimResult.publicKey, - body: dkimResult.body, - bodyHash: dkimResult.bodyHash, - message: dkimResult.message, - shaPrecomputeSelector: "How are", - maxMessageLength: 640, + const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult(dkimResult, { + maxHeadersLength: 640, maxBodyLength: 768, }); emailVerifierInputs.emailHeader[640 - 1] = "1"; @@ -156,14 +110,10 @@ describe("EmailVerifier", () => { const invalidBody = Buffer.from(dkimResult.body); invalidBody[invalidBody.length - 1] = 1; - const emailVerifierInputs = generateCircuitInputs({ - rsaSignature: dkimResult.signature, - rsaPublicKey: dkimResult.publicKey, - body: invalidBody, - bodyHash: dkimResult.bodyHash, - message: dkimResult.message, - shaPrecomputeSelector: "How are", - maxMessageLength: 640, + const dkim = { ...dkimResult, body: invalidBody } + + const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult(dkim, { + maxHeadersLength: 640, maxBodyLength: 768, }); @@ -177,14 +127,8 @@ describe("EmailVerifier", () => { }); it("should fail if body padding is tampered", async function () { - const emailVerifierInputs = generateCircuitInputs({ - rsaSignature: dkimResult.signature, - rsaPublicKey: dkimResult.publicKey, - body: dkimResult.body, - bodyHash: dkimResult.bodyHash, - message: dkimResult.message, - shaPrecomputeSelector: "How are", - maxMessageLength: 640, + const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult(dkimResult, { + maxHeadersLength: 640, maxBodyLength: 768, }); emailVerifierInputs.emailBody![768 - 1] = "1"; @@ -201,14 +145,10 @@ describe("EmailVerifier", () => { it("should fail if body hash is tampered", async function () { const invalidBodyHash = dkimResult.bodyHash + "a"; - const emailVerifierInputs = generateCircuitInputs({ - rsaSignature: dkimResult.signature, - rsaPublicKey: dkimResult.publicKey, - body: dkimResult.body, - bodyHash: invalidBodyHash, - message: dkimResult.message, - shaPrecomputeSelector: "How are", - maxMessageLength: 640, + const dkim = { ...dkimResult, bodyHash: invalidBodyHash } + + const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult(dkim, { + maxHeadersLength: 640, maxBodyLength: 768, }); @@ -222,14 +162,9 @@ describe("EmailVerifier", () => { }); it("should produce dkim pubkey hash correctly", async function () { - const emailVerifierInputs = generateCircuitInputs({ - rsaSignature: dkimResult.signature, - rsaPublicKey: dkimResult.publicKey, - body: dkimResult.body, - bodyHash: dkimResult.bodyHash, - message: dkimResult.message, + const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult(dkimResult, { shaPrecomputeSelector: "How are", - maxMessageLength: 640, + maxHeadersLength: 640, maxBodyLength: 768, }); @@ -272,14 +207,9 @@ describe("EmailVerifier : Without body check", () => { }); it("should verify email when ignore_body_hash_check is true", async function () { - // The result wont have shaPrecomputeSelector, maxMessageLength, maxBodyLength, ignoreBodyHashCheck - const emailVerifierInputs = generateCircuitInputs({ - rsaSignature: dkimResult.signature, - rsaPublicKey: dkimResult.publicKey, - body: dkimResult.body, - bodyHash: dkimResult.bodyHash, - message: dkimResult.message, - maxMessageLength: 640, + // The result wont have shaPrecomputeSelector, maxHeadersLength, maxBodyLength, ignoreBodyHashCheck + const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult(dkimResult, { + maxHeadersLength: 640, maxBodyLength: 768, ignoreBodyHashCheck: true, }); diff --git a/packages/circuits/tests/rsa.test.ts b/packages/circuits/tests/rsa.test.ts index 5a49510ab..4f6481b4e 100644 --- a/packages/circuits/tests/rsa.test.ts +++ b/packages/circuits/tests/rsa.test.ts @@ -1,17 +1,15 @@ import fs from "fs"; import path from "path"; import { wasm as wasm_tester } from "circom_tester"; -import { DKIMVerificationResult } from "@zk-email/helpers/src/dkim"; -import { generateCircuitInputs } from "@zk-email/helpers/src/input-helpers"; -import { verifyDKIMSignature } from "@zk-email/helpers/src/dkim"; -import { toCircomBigIntBytes } from "@zk-email/helpers/src/binaryFormat"; +import { generateEmailVerifierInputs } from "@zk-email/helpers/src/input-generators"; +import { toCircomBigIntBytes } from "@zk-email/helpers/src/binary-format"; describe("RSA", () => { jest.setTimeout(10 * 60 * 1000); // 10 minutes let circuit: any; - let dkimResult: DKIMVerificationResult; + let rawEmail: Buffer; beforeAll(async () => { circuit = await wasm_tester( @@ -22,18 +20,12 @@ describe("RSA", () => { // output: path.join(__dirname, "./compiled-test-circuits"), } ); - const rawEmail = fs.readFileSync(path.join(__dirname, "./test-emails/test.eml")); - dkimResult = await verifyDKIMSignature(rawEmail); + rawEmail = fs.readFileSync(path.join(__dirname, "./test-emails/test.eml")); }); it("should verify 2048 bit rsa signature correctly", async function () { - const emailVerifierInputs = generateCircuitInputs({ - rsaSignature: dkimResult.signature, - rsaPublicKey: dkimResult.publicKey, - body: dkimResult.body, - bodyHash: dkimResult.bodyHash, - message: dkimResult.message, - maxMessageLength: 640, + const emailVerifierInputs = await generateEmailVerifierInputs(rawEmail, { + maxHeadersLength: 640, maxBodyLength: 768, }); @@ -72,13 +64,8 @@ describe("RSA", () => { }); it("should fail when verifying with an incorrect signature", async function () { - const emailVerifierInputs = generateCircuitInputs({ - rsaSignature: dkimResult.signature, - rsaPublicKey: dkimResult.publicKey, - body: dkimResult.body, - bodyHash: dkimResult.bodyHash, - message: dkimResult.message, - maxMessageLength: 640, + const emailVerifierInputs = await generateEmailVerifierInputs(rawEmail, { + maxHeadersLength: 640, maxBodyLength: 768, }); diff --git a/packages/circuits/tests/sha.test.ts b/packages/circuits/tests/sha.test.ts index f815ee587..82e8623d6 100644 --- a/packages/circuits/tests/sha.test.ts +++ b/packages/circuits/tests/sha.test.ts @@ -1,7 +1,7 @@ import { wasm as wasm_tester } from "circom_tester"; import path from "path"; -import { sha256Pad, shaHash } from "@zk-email/helpers/src/shaHash"; -import { Uint8ArrayToCharArray, uint8ToBits } from "@zk-email/helpers/src/binaryFormat"; +import { sha256Pad, shaHash } from "@zk-email/helpers/src/sha-utils"; +import { Uint8ArrayToCharArray, uint8ToBits } from "@zk-email/helpers/src/binary-format"; describe("SHA256 for email header", () => { diff --git a/packages/helpers/src/input-generators.ts b/packages/helpers/src/input-generators.ts index 9bac2988c..6da62ca3b 100644 --- a/packages/helpers/src/input-generators.ts +++ b/packages/helpers/src/input-generators.ts @@ -17,7 +17,7 @@ type CircuitInput = { type InputGenerationArgs = { ignoreBodyHashCheck?: boolean; shaPrecomputeSelector?: string; - maxHeaderLength?: number; // Max length of the email header including padding + maxHeadersLength?: number; // Max length of the email header including padding maxBodyLength?: number; // Max length of the email body after shaPrecomputeSelector including padding }; @@ -58,7 +58,7 @@ export function generateEmailVerifierInputsFromDKIMResult( // SHA add padding const [messagePadded, messagePaddedLen] = sha256Pad( headers, - params.maxHeaderLength || MAX_HEADER_PADDED_BYTES + params.maxHeadersLength || MAX_HEADER_PADDED_BYTES ); const circuitInputs: CircuitInput = { diff --git a/packages/helpers/tests/test-data/email-good-large.eml b/packages/helpers/tests/test-data/email-good-large.eml new file mode 100644 index 000000000..8b90fbb4c --- /dev/null +++ b/packages/helpers/tests/test-data/email-good-large.eml @@ -0,0 +1,104 @@ +Delivered-To: zkewtest@gmail.com +Received: by 2002:a05:6358:a16:b0:17f:783c:f0aa with SMTP id 22csp4286030rwa; + Wed, 3 Apr 2024 03:54:05 -0700 (PDT) +X-Google-Smtp-Source: AGHT+IGmRURImygOH4f9VqzPmRe9AUmKNfQTHgpnvsfn3zt5dtOy1sXyktb+GAdRquRhEddZGr+d +X-Received: by 2002:a17:902:f612:b0:1e2:9ae0:2165 with SMTP id n18-20020a170902f61200b001e29ae02165mr580119plg.0.1712141645481; + Wed, 03 Apr 2024 03:54:05 -0700 (PDT) +ARC-Seal: i=1; a=rsa-sha256; t=1712141645; cv=none; + d=google.com; s=arc-20160816; + b=ayDjXuFlNYGRpSTmS7LRwY0taBw2d3jgmRonLAk2zjj5FQaxdkTE9VIsqf70GLVIkV + jSOtbp3fxhqGS4G+skUL2/mfW3Z6cItiIx9i6SEukBwdYfLghh1uLZcS+IUZePeAvzB/ + 3MsCu0oKoRJo2YS4sqbOYpsYvSH4vxrjMete5iA7txSsVPhLa2RZqGiAbzusouIHUXA4 + qMmEIcbZN3PD918cB/WpjN2/0roQAnMF2r4zAYtFSDJ9vL+IqFqIF+v3ti00QWTJpQqg + Yg25KqIXyV9XouiUNhG1T7QArZT3wQCdvVToa5F9p2akSOmKyRrYUZ9aNgO2YksrSqum + XnSQ== +ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; + h=to:date:message-id:subject:mime-version:content-transfer-encoding + :from:dkim-signature; + bh=2JsdK4BMzzt9w4Zlz2TdyVCFc+l7vNyT5aAgGDYf7fM=; + fh=pY1mLRy3Ij9Q2g+iSj0cDBFssb6ix/XdcltTP/vwMGg=; + b=RCLt30e366sNP/g5xR1KXDXfkEFsnY0w2VvbDBXLYZcX16CVyNTpXCbM6yQFD7D2i3 + vwB7U4xBirBfX9ikRK+CWozS6ES+xPe/NyjInjC6Fe3ptbb2vFZc6sSQDPJp2AdsTUGI + oY577rydz9/XvVSEeBYtMG5iSZpi+K16ejMViRVi0e+YfXjogylED70xFBYNsvyD+Fb+ + 0KsdhPxg4oXWyRJdgESQ1XncDFpuV8ezyqJiUTcIuWqBtYpbzd+ENBwNMSTsPAPkOcl8 + jbxMZTkhkrHHKxuPg9cezlvcWXnB65PMBTs7g2djhZR4/wreeoRIt9yPwRk6vzirOdFq + er1g==; + dara=google.com +ARC-Authentication-Results: i=1; mx.google.com; + dkim=pass header.i=@icloud.com header.s=1a1hai header.b=ufWPnpZp; + spf=pass (google.com: domain of runnier.leagues.0j@icloud.com designates 17.58.6.45 as permitted sender) smtp.mailfrom=runnier.leagues.0j@icloud.com; + dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=icloud.com +Return-Path: +Received: from pv50p00im-ztdg10021201.me.com (pv50p00im-ztdg10021201.me.com. [17.58.6.45]) + by mx.google.com with ESMTPS id jx10-20020a170903138a00b001e2791673a6si2728483plb.372.2024.04.03.03.54.05 + for + (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); + Wed, 03 Apr 2024 03:54:05 -0700 (PDT) +Received-SPF: pass (google.com: domain of runnier.leagues.0j@icloud.com designates 17.58.6.45 as permitted sender) client-ip=17.58.6.45; +Authentication-Results: mx.google.com; + dkim=pass header.i=@icloud.com header.s=1a1hai header.b=ufWPnpZp; + spf=pass (google.com: domain of runnier.leagues.0j@icloud.com designates 17.58.6.45 as permitted sender) smtp.mailfrom=runnier.leagues.0j@icloud.com; + dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=icloud.com +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=icloud.com; + s=1a1hai; t=1712141644; + bh=2JsdK4BMzzt9w4Zlz2TdyVCFc+l7vNyT5aAgGDYf7fM=; + h=from:Content-Type:Mime-Version:Subject:Message-Id:Date:to; + b=ufWPnpZp4PTzCGzOEYf9RPBb8uW2JmwKyI38czxBT6sJA+SFY+X0pTZaxcvYiGw6x + jEMCEoLEm/NUiXCCKKVBN1QTxRCPyzmWyrDiGk8xexXhCK+5I6r45D8N7QaFk6ue6F + gLq7FtaOf5iaLKO2gqsB2Jc+rMrYK8ehw0kk/uDtKHXI1Dl3pJgg12HSPEdzJhXAMP + +onsUWJ7TFu0gdX5lrSIXNgEQh+zvmSFWnPuoMzHKEca6eu67N/Q5WWVUPm32mlp6q + MCswRkPn9hF72T4566YoL6l4vqCgh5Jwnto1ae59pJ/2z3wuECS2EWWVDJ/GTwzALf + JkC4yhhw40NLQ== +Received: from smtpclient.apple (pv50p00im-dlb-asmtp-mailmevip.me.com [17.56.9.10]) + by pv50p00im-ztdg10021201.me.com (Postfix) with ESMTPSA id 264B86801D8 + for ; Wed, 3 Apr 2024 10:54:02 +0000 (UTC) +from: runnier.leagues.0j@icloud.com +Content-Type: text/plain; + charset=utf-8 +Content-Transfer-Encoding: quoted-printable +Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3731.500.231\)) +Subject: Bitcoin +Message-Id: <12800A90-4ECC-4513-9298-A33546122920@me.com> +Date: Wed, 3 Apr 2024 16:23:48 +0530 +to: zkewtest@gmail.com +X-Mailer: Apple Mail (2.3731.500.231) +X-CLX-Shades: Junk +X-CLX-UShades: None +X-CLX-Score: -186 +X-CLX-UnSpecialScore: None +X-CLX-Spam: true +X-MANTSH: 1TFkXBxsSHBEKWUQXa3xDUFkaX1wbQUURCllNF2BfREERCllJFwcZGnEbBgccGnc + GBxsaEgYaBhoGGgYHHhJxGhAYdwYaBhoGGgYaBhoGGnEaEBp3BhoRClleF2NjeREKQ04XUER5Z + 0QdZUJDGXxvfURnGW9EQV1TQEZCU2dpQUUfGk4RClhcFxkEGgQfGgUbGhoEHRoEGxMSBBsfEBs + eGh8aEQpeWRdOe15ERxEKTFoXTX5rb2sRCkVZF29raxEKTU4XaREKQ1oXGxoZBBgYHQQfGAQcH + REKQl4XGxEKXk4XGxEKQkUXYE1beBtQX38YcGcRCkJOF2xwYHlAHWJSaRpiEQpCTBdgTVt4G1B + ffxhwZxEKQm4XbWVwG0JrYh5eZlwRCkJsF2BNW3gbUF9/GHBnEQpCQBdgcwEaYllCS0F/UBEKQ + lgXbUxpZ2xhUmBBbm0RCkVDFxsRCnBoF2MSBR1nbWdwYhJhEAcZGhEKcGgXbXhdHxpjS1B5bnA + QBxkaEQpwaBdhZRJJQ1pjS29nHxAHGRoRCnBoF2diRFlzG0gTXUAFEAcZGhEKcGgXbGB+c3hJW + XJMQRoQBxkaEQpwaBdmT2VecGRbTh1pThAHGRoRCnBoF2xfb39OGhxJRUlAEAcZGhEKcGgXa09 + EBXgbZBN8ZEwQBxkaEQpwTBdrchpQWkQeZFtScBAHGRoRCm1+FxoRClhNF0sR +X-Proofpoint-GUID: znSMn7Ohi3VEWnM3EnkwyjlhyMCko50d +X-Proofpoint-ORIG-GUID: znSMn7Ohi3VEWnM3EnkwyjlhyMCko50d +X-Proofpoint-Virus-Version: vendor=baseguard + engine=ICAP:2.0.272,Aquarius:18.0.1011,Hydra:6.0.619,FMLib:17.11.176.26 + definitions=2024-04-03_10,2024-04-01_01,2023-05-22_02 +X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=0 bulkscore=0 + malwarescore=0 adultscore=0 mlxscore=0 phishscore=0 clxscore=-186 + spamscore=0 mlxlogscore=495 classifier=spam adjust=0 reason=mlx + scancount=1 engine=8.19.0-2308100000 definitions=main-2404030075 + +The Times 03/Jan/2009 Chancellor on brink of second bailout for banks + +15 years ago, Satoshi mined the first block of the Bitcoin blockchain = +After the Bitcoin white paper appeared on October 31, 2008, on a = +cryptography mailing list, the Genesis Block =E2=80=94 the first bitcoin = +block and the basis of the entire Bitcoin trading system in place to = +this day =E2=80=94 was mined on January 3, 2009.=20 + +The Genesis Block is also known as Block 0 or Block 1, and is still in = +the Bitcoin network, where it will remain as long as there is a computer = +running the Bitcoin software.=20 + +All nodes in the Bitcoin network can consult it, even if it is at the = +other end of the network with hundreds of thousands of blocks. +