لنفرض أن لديك شركة ناشئة تبني منصة لشهادات الضمان: المصنع يسجل الجهاز برقمه التسلسلي، والموزع يسجل بيعه للمتجر، والمتجر يسجل بيعه للعميل فيبدأ الضمان، ومركز الصيانة يسجل كل إصلاح، والعميل يفتح التطبيق فيرى تاريخ جهازه كاملاً. وفي اجتماع مع أحد الموزعين الكبار يقول مدير المشتريات عندهم: «نريد أن تكون الشهادات على البلوكتشين، حتى لا يستطيع أحد أن يغيرها». ثم يرسل لك المدير التنفيذي بعد الاجتماع رسالة قصيرة: «هل نحتاجه فعلاً؟ وكم يكلف؟ أريد الجواب قبل نهاية الأسبوع». وهذا السؤال يتكرر في كل شركة تقريباً، والجواب الصحيح عنه لا يأتي من حماس الاجتماع ولا من رفض التقنية مسبقاً، وإنما من أسئلة محددة عن من يكتب، ومن يثق بمن، وماذا يحدث للبيانات بعد خمس سنوات.
وفي مقال البلوكتشين للمبتدئين وضعنا خمسة أسئلة مختصرة في قسم «متى لا تحتاج بلوكتشين»، وهذا المقال هو النسخة العملية الكاملة منها: نأخذ كل سؤال على حدة، ونرى ما الذي يعنيه في منصة الضمان، وما البديل الأرخص الذي يحل نفس المشكلة، ثم نبني هذا البديل بأيدينا ونجربه، ونقيس ما يكلفه الخيار الآخر على شبكة Ethereum محلية. والمقال تعليمي تقني، ولا يتضمن أي نصيحة استثمارية أو ترويجاً لأي عملة أو منصة.
وسوف نناقش في هذا المقال ما يلي:
- ماذا يعطيك البلوكتشين بالضبط، وماذا يكلفك، حتى تعرف ما الذي تشتريه.
- الأسئلة الخمسة واحداً بعد الآخر: الكتاب المستقلون، والجهة الموثوقة، وهل يكفي إظهار التعديل Tamper Evidence، والبيانات الشخصية، والتحقق العلني.
- تجربة عملية: سجل يظهر التعديل في PostgreSQL بسلسلة هاش، ونقاط تحقق موقعة عند الشركاء تكشف إعادة الكتابة الكاملة.
- نفس الأحداث في عقد ذكي على شبكة Anvil المحلية: الغاز Gas والكتل، ثم تجربة «حذف» رقم جوال من السلسلة.
- حالتان موثقتان من الواقع: منصة TradeLens، ومشروع استبدال CHESS في بورصة أستراليا.
- مقارنة بإطار NIST وورقة «Do you need a Blockchain?»، والأخطاء الشائعة، وورقة قرار تستخدمها مع فريقك.
ماذا تشتري عندما تختار البلوكتشين؟
قبل الأسئلة يجب أن نتفق على ما يعطيه البلوكتشين فعلاً، لأن كثيراً من النقاشات تفشل لأن كل طرف يقصد شيئاً مختلفاً. والبلوكتشين في جوهره سجل يكتب فيه عدة أطراف، وتحفظ كل عقدة Node نسخة كاملة منه، وتتفق العقد على ترتيب المعاملات بخوارزمية إجماع Consensus، وكل معاملة موقعة من صاحبها، وكل كتلة تحمل هاش الكتلة التي قبلها، وقد شرحنا هذه اللبنات في المقال الأول والإجماع بالتفصيل في مقال الإجماع. ومن هذه اللبنات تأتي ثلاثة ضمانات لا تحصل عليها من قاعدة بيانات واحدة يشغلها طرف واحد:
- لا يوجد مشغل يستطيع أن يغير الماضي وحده: لأن النسخ عند أطراف مستقلة، وأي تغيير يحتاج موافقة أغلبهم.
- لا يوجد مشغل يستطيع أن يرفض معاملة صحيحة وحده: في الشبكة العامة أي عقدة تستطيع أن تمرر المعاملة، وفي الشبكة المصرح بها تحتاج أغلبية لترفضها.
- التحقق المستقل: كل طرف يشغل عقدته ويتحقق من كل معاملة بنفسه، ولا يعتمد على شاشة يعرضها عليه طرف آخر.
وفي المقابل تدفع ثمناً ثابتاً لا تستطيع أن تلغيه بالتصميم الجيد، والسبب أنه جزء من طريقة عمل التقنية نفسها: كل عقدة تنفذ كل معاملة وتحفظها، فالكتابة أبطأ وأغلى من قاعدة بيانات، وكل عضو يرى البيانات إلا إذا أضفت طبقات خصوصية تزيد التعقيد، والحذف والتصحيح صعبان أو مستحيلان، وكل طرف يحتاج إدارة مفاتيح Key Management، وكل تغيير في قواعد الشبكة يحتاج اتفاق الأعضاء أو حوكمة Governance مكتوبة. لذلك السؤال الصحيح ليس «هل البلوكتشين أفضل؟» وإنما «هل نحتاج هذه الضمانات الثلاثة تحديداً، وهل نقبل هذا الثمن من أجلها؟».
ويصف تقرير NIST IR 8202 في القسم 8 الخطأ الذي يقع فيه أغلب الناس، فيقول إن كثيراً من المؤسسات تبدأ بسؤال «نريد أن نستخدم البلوكتشين في مكان ما، فأين؟»، وإن هذا يقود إلى الإحباط لأن التقنية لا تصلح لكل شيء، وإن الأفضل أن تفهم التقنية أولاً ثم تبحث عن الأنظمة التي تناسبها. وطلب الموزع في قصتنا هو بالضبط من النوع الأول: هو لا يريد بلوكتشين، وإنما يريد ألا يستطيع أحد أن يغير الشهادات، وهذا متطلب يمكن تحقيقه بأكثر من طريقة. والصورة التالية تبين الأسئلة الخمسة التي سوف نمشي عليها والمخرج عند كل سؤال:

السؤال الأول: هل تكتب عدة أطراف مستقلة في نفس السجل؟
هذا السؤال يبدو سهلاً، والإجابة الأولى التي يعطيها الفريق دائماً هي «نعم، عندنا مصنع وموزع ومتجر ومركز صيانة»، ولكن انتبه للكلمة المهمة فيه: مستقلة. فالسؤال ليس كم جهة تستخدم النظام، وإنما كم جهة تكتب في السجل بمفتاحها وعقدتها، دون أن تمر على تطبيق طرف آخر. وفي منصة الضمان كما هي اليوم، المصنع والموزع والمتجر ومركز الصيانة كلهم يكتبون عبر واجهة API التي تشغلها شركتك، وبالتالي الكاتب الحقيقي في قاعدة البيانات واحد فقط، وهو تطبيقك، والآخرون مستخدمون له مهما كان عددهم.
وقد يتساءل البعض: ما الفرق إذا كان كل واحد منهم يسجل الدخول بحسابه؟ والإجابة أن الفرق في من يملك القدرة على التعديل: تطبيقك يستطيع أن يكتب أي شيء باسم أي مستخدم، ومدير قاعدة البيانات عندك يستطيع أن يعدل أي سطر، والشركاء يرون فقط ما تعرضه عليهم الواجهة. فإذا كان هذا مقبولاً لهم فأنت في حالة الكاتب الواحد، ولا يوجد ما يتفق عليه أحد مع أحد، والحل هو قاعدة البيانات مع الصلاحيات Permissions وسجل التدقيق Audit Trail الذي شرحناه في مقال سجل التدقيق.
أما إذا كان الشركاء يريدون أن يكتب كل واحد منهم بمفتاحه الخاص، وأن يحتفظ كل واحد بنسخته، فهنا تصبح عندك عدة أطراف مستقلة فعلاً، وتنتقل إلى السؤال الثاني. ولاحظ أن هذا الطلب له ثمن عندهم أيضاً: كل شريك يجب أن يشغل شيئاً، ويحمي مفتاحاً، ويتابع الترقيات، وكثير من الشركاء عندما يعرفون ذلك يفضلون أن يبقوا مستخدمين.
السؤال الثاني: هل توجد جهة يقبل الجميع أن تشغل السجل؟
إذا كان هناك عدة كتاب مستقلين، فالسؤال التالي هو هل يقبلون أن تشغل جهة واحدة السجل نيابة عنهم. وهذا ليس سؤالاً تقنياً، وإنما سؤال عن العلاقات والمصالح والنظام. وأغلب الأنظمة المالية في العالم تعمل بهذا النموذج: البنوك تتنافس فيما بينها، ومع ذلك تقبل كلها أن يشغل البنك المركزي نظام التسوية، كما شرحنا في مقال المقاصة والتسوية، والسبب أن الجهة المشغلة محايدة ومنظمة وعليها رقابة.
وفي منصة الضمان قد تكون الجهة المقبولة هي شركتك نفسها إذا كانت محايدة ولا تبيع أجهزة، أو جمعية تجمع الموزعين، أو جهة حكومية تنظم القطاع. وإذا وجدت هذه الجهة فهي التي تشغل قاعدة البيانات، مع صلاحيات واضحة، ونسخ احتياطي، وسجل تدقيق، وعقود تحدد مسؤوليتها، وهذا أرخص وأسرع وأسهل في التصحيح من أي شبكة. أما إذا كان الشركاء متنافسين ولا يقبل أي منهم أن يملك الآخر السجل، ولا توجد جهة محايدة يقبلونها، فهنا فقط يصبح السجل المشترك دون مالك مطروحاً، وهذه بالضبط الحالة التي تعثرت فيها أشهر منصة بلوكتشين في الشحن، كما سوف نرى في قسم الحالات الموثقة.
السؤال الثالث: هل يكفي أن يكتشف كل طرف التعديل بنفسه؟
هذا هو السؤال الذي يغيب عن أغلب النقاشات، وهو الأهم في قصتنا. فالموزع قال «حتى لا يستطيع أحد أن يغيرها»، ولكن هل يحتاج فعلاً أن يكون التغيير مستحيلاً، أم يكفيه أن يكون أي تغيير ظاهراً له فوراً؟ والفرق بين الاثنين كبير: منع التعديل Tamper Resistance يحتاج أن تكون النسخ عند أطراف مستقلة وأن يتفقوا على كل كتابة، أما إظهار التعديل Tamper Evidence فيكفيه سجل واحد بسلسلة هاش، ونسخة صغيرة موقعة من رأس السجل عند كل شريك. فإذا عدل أحد الماضي ظهر ذلك عند المقارنة، ثم يأتي دور العقد والنظام والمحاكم كما في أي علاقة تجارية.
وهذه الفكرة ليست نظرية: شهادات TLS التي يستخدمها كل موقع على الإنترنت تسجل في سجلات علنية تسمى شفافية الشهادات Certificate Transparency، وهي سجلات لا تقبل إلا الإضافة Append-only مبنية على شجرة Merkle كما في RFC 9162، ويشغل كل سجل جهة واحدة، ويستطيع أي شخص أن يراقبها ويكتشف إذا حاول المشغل أن يغير ما سجله، دون أي إجماع بين المشغلين. وقد شرحنا الشهادات نفسها في مقال الشهادات الرقمية. والآن سوف نبني نسخة صغيرة من هذه الفكرة لمنصة الضمان، ونرى بأعيننا أين تكفي وأين لا تكفي.
تجربة عملية: سجل بسلسلة هاش في PostgreSQL
نشغل PostgreSQL في Container، ونضع ملفات التجربة في مجلد يربط بداخله، وكلمة المرور هنا للتجربة المحلية فقط:
docker run -d --name lab-need-pg -e POSTGRES_PASSWORD=lab-only -v "$PWD:/lab" postgres:18.6-alpine
docker exec -i lab-need-pg psql -U postgres -t -c "select version()"والمخرج سوف يكون كما يلي:
PostgreSQL 18.6 on x86_64-pc-linux-musl, compiled by gcc (Alpine 15.2.0) 15.2.0, 64-bitوالملف التالي ينشئ جدول أحداث الضمان، ودالة Trigger تحسب لكل صف جديد هاش SHA-256 من هاش الصف الذي قبله مع محتوى الصف نفسه، باستخدام إضافة pgcrypto. احفظه باسم 01-ledger.sql:
-- A tamper-evident event log: every row stores the hash of the row before it.
CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE TABLE warranty_events (
id bigserial PRIMARY KEY,
party text NOT NULL, -- who wrote the event (factory, distributor, service centre)
serial_no text NOT NULL, -- device serial number
event text NOT NULL, -- sold, repaired, replaced ...
created_at timestamptz NOT NULL DEFAULT now(),
prev_hash bytea NOT NULL,
row_hash bytea NOT NULL
);
CREATE FUNCTION chain_event() RETURNS trigger AS $$
DECLARE last bytea;
BEGIN
-- one writer at a time, so two inserts never take the same prev_hash
PERFORM pg_advisory_xact_lock(42);
SELECT row_hash INTO last FROM warranty_events ORDER BY id DESC LIMIT 1;
NEW.prev_hash := coalesce(last, '\x00'::bytea);
NEW.row_hash := digest(NEW.prev_hash || convert_to(
NEW.party || '|' || NEW.serial_no || '|' || NEW.event || '|' || NEW.created_at::text, 'UTF8'), 'sha256');
RETURN NEW;
END $$ LANGUAGE plpgsql;
CREATE TRIGGER chain_before_insert BEFORE INSERT ON warranty_events
FOR EACH ROW EXECUTE FUNCTION chain_event();
-- nobody edits or deletes through the application role
REVOKE UPDATE, DELETE ON warranty_events FROM PUBLIC;في الإعداد أعلاه لاحظ التالي:
- الدالة
pg_advisory_xact_lock(42)تجعل الإدراجات تمر واحداً بعد الآخر، والسبب أن معاملتين متزامنتين قد تقرآن نفس آخر صف فتأخذان نفسprev_hash، فتتفرع السلسلة. والقفل يتحرر تلقائياً عند نهاية المعاملة كما في توثيق الأقفال، وثمنه أن الكتابة في هذا الجدول تصبح متسلسلة. - الهاش يحسب من نص ثابت الترتيب تفصل حقوله العلامة
|، حتى يحسبه أي طرف بنفس الطريقة لاحقاً، وهي نفس فكرةsort_keysفي المقال الأول. - الصف الأول لا يوجد قبله صف، فيأخذ القيمة
\x00بدلاً من هاش سابق، مثل كتلة التكوين Genesis Block.
ننفذ الملف ثم نضيف أربعة أحداث لجهاز واحد، كل حدث من جهة مختلفة:
docker exec -i lab-need-pg psql -U postgres -v ON_ERROR_STOP=1 -f /lab/01-ledger.sql
docker exec -i lab-need-pg psql -U postgres -v ON_ERROR_STOP=1 <<'EOF'
INSERT INTO warranty_events (party, serial_no, event) VALUES
('factory', 'SN-1001', 'manufactured'),
('distributor', 'SN-1001', 'sold to retailer'),
('retailer', 'SN-1001', 'sold to customer, warranty starts'),
('service', 'SN-1001', 'screen repaired under warranty');
SELECT id, party, event, encode(prev_hash,'hex')::char(12) AS prev, encode(row_hash,'hex')::char(12) AS hash FROM warranty_events ORDER BY id;
EOFوالمخرج سوف يكون كما يلي:
CREATE EXTENSION
CREATE TABLE
CREATE FUNCTION
CREATE TRIGGER
REVOKE
INSERT 0 4
id | party | event | prev | hash
----+-------------+-----------------------------------+--------------+--------------
1 | factory | manufactured | 00 | c268317b14d6
2 | distributor | sold to retailer | c268317b14d6 | 1b49b8f0f6c7
3 | retailer | sold to customer, warranty starts | 1b49b8f0f6c7 | 3b6c6a12a90d
4 | service | screen repaired under warranty | 3b6c6a12a90d | a51bed56a0d2
(4 rows)لاحظ أن عمود prev في كل صف هو نفس عمود hash في الصف الذي قبله، تماماً مثل حقل parentHash في كتل Ethereum. والهاشات عندك سوف تختلف لأن وقت الإدراج جزء من المحتوى. والآن نحتاج استعلاماً يعيد حساب كل الهاشات ويقارنها بالمحفوظ، احفظه باسم 02-verify.sql:
-- Recompute every hash and compare it with what is stored.
WITH r AS (
SELECT id, prev_hash, row_hash,
lag(row_hash) OVER (ORDER BY id) AS real_prev,
digest(prev_hash || convert_to(party || '|' || serial_no || '|' || event || '|' || created_at::text, 'UTF8'), 'sha256') AS recomputed
FROM warranty_events)
SELECT count(*) AS rows,
count(*) FILTER (WHERE recomputed <> row_hash) AS bad_content,
count(*) FILTER (WHERE real_prev IS NOT NULL AND real_prev <> prev_hash) AS bad_links,
min(id) FILTER (WHERE recomputed <> row_hash OR (real_prev IS NOT NULL AND real_prev <> prev_hash)) AS first_bad_id
FROM r;docker exec -i lab-need-pg psql -U postgres -f /lab/02-verify.sqlوالمخرج سوف يكون كما يلي:
rows | bad_content | bad_links | first_bad_id
------+-------------+-----------+--------------
4 | 0 | 0 |
(1 row)الدالة lag تعطي هاش الصف السابق فعلاً، فنقارنه بما حفظه الصف في prev_hash، ونعيد حساب هاش كل صف من محتواه ونقارنه بما في row_hash، والنتيجة صفر في الحالتين، أي أن السلسلة سليمة.
ماذا يحدث إذا حاول التطبيق أن يعدل؟
أول خط دفاع لا علاقة له بالهاش، وإنما بالصلاحيات: ننشئ دوراً للتطبيق يستطيع أن يقرأ ويضيف فقط، ثم نجرب التعديل والحذف به:
docker exec -i lab-need-pg psql -U postgres <<'EOF'
CREATE ROLE app LOGIN PASSWORD 'lab-only';
GRANT SELECT, INSERT ON warranty_events TO app;
GRANT USAGE ON SEQUENCE warranty_events_id_seq TO app;
EOF
docker exec -i -e PGPASSWORD=lab-only lab-need-pg psql -h 127.0.0.1 -U app -d postgres -c "UPDATE warranty_events SET event = 'x' WHERE id = 2;"
docker exec -i -e PGPASSWORD=lab-only lab-need-pg psql -h 127.0.0.1 -U app -d postgres -c "DELETE FROM warranty_events WHERE id = 2;"والمخرج سوف يكون كما يلي:
CREATE ROLE
GRANT
GRANT
ERROR: permission denied for table warranty_events
ERROR: permission denied for table warranty_eventsهذا يحمي من خطأ في الكود أو من مخترق وصل إلى التطبيق، ولكنه لا يحمي من صاحب صلاحية أعلى، وهو بالضبط ما يخشاه الموزع في قصتنا: مدير قاعدة البيانات عندك، أو موظف عندك تحت ضغط عميل كبير.
نجرب العطل: مدير قاعدة البيانات يعدل حدثاً
لنفرض أن مركز الصيانة أصلح الشاشة تحت الضمان، ثم أراد أحدهم لاحقاً أن يقول إن الإصلاح كان على حساب العميل. نعدل الصف 4 بحساب postgres الذي يملك كل الصلاحيات، ثم نشغل التحقق:
docker exec -i lab-need-pg psql -U postgres -c "UPDATE warranty_events SET event = 'screen repaired, paid by customer' WHERE id = 4;"
docker exec -i lab-need-pg psql -U postgres -f /lab/02-verify.sqlوالمخرج سوف يكون كما يلي:
UPDATE 1
rows | bad_content | bad_links | first_bad_id
------+-------------+-----------+--------------
4 | 1 | 0 | 4
(1 row)التحقق كشف التعديل وحدد الصف بالضبط، لأن المحتوى الجديد لا يطابق الهاش المحفوظ. ولكن مدير قاعدة البيانات يعرف كيف تحسب الدالة الهاش، ويستطيع أن يحسبه بنفسه. احفظ الملف التالي باسم 03-rewrite.sql:
-- What an administrator with full access can do: recompute the hash of the edited row (and of every row after it).
UPDATE warranty_events
SET row_hash = digest(prev_hash || convert_to(party || '|' || serial_no || '|' || event || '|' || created_at::text, 'UTF8'), 'sha256')
WHERE id = 4;docker exec -i lab-need-pg psql -U postgres -f /lab/03-rewrite.sql
docker exec -i lab-need-pg psql -U postgres -f /lab/02-verify.sqlوالمخرج سوف يكون كما يلي:
UPDATE 1
rows | bad_content | bad_links | first_bad_id
------+-------------+-----------+--------------
4 | 0 | 0 |
(1 row)السلسلة تبدو سليمة تماماً، والتحقق الداخلي لم يكتشف شيئاً. وفي حالتنا عدلنا الصف الأخير فقط، ولو كان الصف في المنتصف لاحتاج أن يعيد حساب كل الصفوف بعده، وهذا استعلام واحد أيضاً على جهازه. وهذه هي نفس النتيجة التي رأيناها مع chain.py في المقال الأول: السلسلة وحدها تجعل التعديل ظاهراً لمن يقارن، ولا تمنع من يملك كل النسخ من إعادة كتابتها.
نقاط التحقق الموقعة Signed Checkpoints عند الشركاء
الحل البسيط الذي لا يحتاج أي شبكة: في نهاية كل يوم يوقع المشغل رأس السجل، أي رقم آخر صف وهاشه، بمفتاحه الخاص، ويرسل التوقيع إلى كل شريك فيحفظه عنده. فإذا أعاد أحد كتابة الماضي لاحقاً، فلن يطابق الهاش الحالي الهاش الذي وقعه المشغل وحفظه الشريك، ولا يستطيع المشغل أن ينكر توقيعه. والملف التالي يحاكي الطرفين بتوقيع Ed25519، احفظه باسم checkpoint.py:
# The operator signs the head of the log; every partner keeps a copy of the signed checkpoint.
import json, sys
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey, Ed25519PublicKey
from cryptography.hazmat.primitives import serialization as s
cmd = sys.argv[1]
if cmd == "sign": # operator side: sign "row id + hash" once a day
row_id, head = sys.argv[2], sys.argv[3]
key = Ed25519PrivateKey.generate()
msg = f"warranty-log head id={row_id} hash={head}".encode()
cp = {"id": row_id, "hash": head, "sig": key.sign(msg).hex(),
"pub": key.public_key().public_bytes(s.Encoding.Raw, s.PublicFormat.Raw).hex()}
json.dump(cp, open("checkpoint.json", "w"), indent=1)
print("checkpoint saved by the partner:", cp["hash"][:16], "sig", cp["sig"][:16] + "...")
elif cmd == "check": # partner side: compare the live log with the copy it kept
cp = json.load(open("checkpoint.json"))
live = sys.argv[2]
pub = Ed25519PublicKey.from_public_bytes(bytes.fromhex(cp["pub"]))
pub.verify(bytes.fromhex(cp["sig"]), f"warranty-log head id={cp['id']} hash={cp['hash']}".encode())
print("signature on checkpoint: valid (the operator did sign this head)")
print("live hash of row", cp["id"], ":", live[:16])
print("hash in checkpoint :", cp["hash"][:16])
print("RESULT:", "log matches" if live == cp["hash"] else "LOG WAS REWRITTEN after the checkpoint")والترتيب الصحيح للتجربة أن نوقع نقطة التحقق قبل التعديل، أي بعد إدراج الصفوف الأربعة مباشرة، فنقرأ هاش الصف 4 ونوقعه:
H=$(docker exec -i lab-need-pg psql -U postgres -At -c "select encode(row_hash,'hex') from warranty_events where id=4")
echo $H
docker run --rm --name lab-need-py -v "$PWD:/lab" -w /lab python:3.13.16-slim \
bash -c "pip install -q --root-user-action=ignore cryptography==46.0.3 && python checkpoint.py sign 4 $H"والمخرج سوف يكون كما يلي، بعد سطري تنبيه من pip:
a51bed56a0d2426f6912b90bc05d004f279fc0856dd89e733ff58298008ad8f2
checkpoint saved by the partner: a51bed56a0d2426f sig 538fa9649b0235a5...ثم بعد التعديل وإعادة حساب الهاش، يقرأ الشريك الهاش الحالي للصف 4 ويقارنه بما عنده:
H=$(docker exec -i lab-need-pg psql -U postgres -At -c "select encode(row_hash,'hex') from warranty_events where id=4")
docker run --rm --name lab-need-py -e PIP_DISABLE_PIP_VERSION_CHECK=1 -v "$PWD:/lab" -w /lab python:3.13.16-slim \
bash -c "pip install -q --root-user-action=ignore cryptography==46.0.3 && python checkpoint.py check $H"والمخرج سوف يكون كما يلي:
signature on checkpoint: valid (the operator did sign this head)
live hash of row 4 : 76ed8ce641a2d7bf
hash in checkpoint : a51bed56a0d2426f
RESULT: LOG WAS REWRITTEN after the checkpointفي الإعداد أعلاه لاحظ التالي:
- التوقيع على نقطة التحقق صحيح، أي أن المشغل نفسه وقع على الهاش
a51bed56…في ذلك اليوم، ولا يستطيع أن يدعي أن الشريك اخترعه. - الهاش الحالي للصف 4 أصبح
76ed8ce6…، والتحقق الداخلي يقول إن السلسلة سليمة، ولكن المقارنة مع النسخة الخارجية تكشف أن السجل أعيدت كتابته بعد التوقيع. - الشريك لم يحتج نسخة من قاعدة البيانات ولا عقدة، وإنما 64 حرفاً وتوقيعاً واحداً لكل يوم. والتطبيق الحقيقي يعطي الشريك أيضاً الصفوف التي تخصه ليتحقق من أنها جزء من السلسلة.
- في هذه التجربة ولدنا مفتاح المشغل وقت التوقيع لتبقى قصيرة، وفي النظام الحقيقي يكون المفتاح ثابتاً ومحمياً، ومفتاحه العام منشوراً عند الشركاء مسبقاً، كما شرحنا في مقال التشفير للمطورين.
والصورة التالية تجمع المراحل الأربع بالقيم الحقيقية:

إذن متى يكفي هذا الحل؟ عندما يكون هدف الشركاء أن يكتشفوا أي تلاعب ويثبتوه، ثم يحلوا الخلاف بالعقود كما يحلون أي خلاف تجاري، وهذه حالة أغلب العلاقات بين الشركات. ومتى لا يكفي؟ عندما يكون اكتشاف التلاعب بعد حدوثه غير مقبول أصلاً، كأن يكون التعديل قادراً على نقل أموال لا يمكن استرجاعها، أو عندما لا يقبل الشركاء أن يستطيع المشغل رفض كتاباتهم أو تأخيرها، وهنا تنتقل إلى السؤال الرابع ثم الخامس.
السؤال الرابع: هل في البيانات ما هو شخصي أو سري أو قد يلزم حذفه؟
في منصة الضمان بيانات شخصية واضحة: اسم العميل ورقم جواله وعنوانه، وكلها تحتاج أن تصحح إذا أخطأ أحد في كتابتها، وأن تحذف عندما تنتهي الحاجة إليها. وفي المملكة يعطي نظام حماية البيانات الشخصية في مادته الرابعة صاحب البيانات حقوقاً منها الحق في طلب تصحيح بياناته وتحديثها، والحق في طلب إتلاف بياناته المتوافرة لدى جهة التحكم مما انتهت الحاجة إليه، وتفاصيل التطبيق في النظام ولوائحه. وتقرير NIST نفسه يذكر أن البلوكتشين لا يعرف من عمليات CRUD إلا الإنشاء والقراءة، وأن المعاملة التي تعلم بيانات قديمة كمحذوفة تترك البيانات نفسها في السلسلة. والآن سوف نرى ذلك عملياً بدلاً من أن نصدقه.
نفس الأحداث في عقد ذكي على Anvil
نكتب عقداً ذكياً صغيراً يسجل نفس أحداث الضمان، ونضيف إليه دالة تحفظ رقم جوال صاحب الجهاز، وهي موجودة هنا فقط لنبين لماذا لا يجب أن تكون في أي عقد حقيقي. أنشئ مجلداً باسم chain فيه الملف src/WarrantyLog.sol:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
// The same warranty events, written to a chain instead of a database table.
contract WarrantyLog {
event Recorded(address indexed party, bytes32 indexed serial, string what);
mapping(bytes32 => string) public lastEvent; // contract storage: every node keeps it
mapping(bytes32 => string) public ownerPhone; // personal data (to show why it must stay off-chain)
function record(bytes32 serial, string calldata what) external {
lastEvent[serial] = what;
emit Recorded(msg.sender, serial, what);
}
function setPhone(bytes32 serial, string calldata phone) external {
ownerPhone[serial] = phone;
}
}والملف foundry.toml في نفس المجلد يثبت إصدار المترجم Compiler حتى يعطي نفس النتيجة كل مرة:
[profile.default]
src = "src"
out = "out"
solc = "0.8.30"ثم نشغل Anvil من Image مجموعة Foundry الرسمية، وهي شبكة Ethereum محلية للتطوير شرحناها في المقال الأول، ونربط المجلد بداخلها:
docker run -d --name lab-need-anvil -v "$PWD/chain:/chain" -w /chain ghcr.io/foundry-rs/foundry:v1.8.6 "anvil --host 0.0.0.0"
docker exec lab-need-anvil forge buildوهنا ظهرت لنا مشكلتان حقيقيتان أثناء التجربة على Docker Desktop في Windows، وقد تظهران لك. الأولى أن الأوامر داخل ال Image تعمل بمستخدم اسمه foundry وليس root، فلم يستطع forge build أن يكتب مجلد out داخل المجلد المربوط، والحل أن تعطي المجلد صلاحية الكتابة بأمر docker exec -u root lab-need-anvil sh -c "chmod -R 777 /chain" في بيئة التجربة فقط. والثانية أن الأدوات التي تكتب ملفاً لكل معاملة، مثل forge script الذي سوف نستخدمه بعد قليل، تصبح بطيئة جداً عندما تكتب في مجلد مربوط من Windows، فنسخنا المشروع إلى /tmp/chain داخل ال Container ونفذنا منه:
docker exec lab-need-anvil sh -c "rm -rf /tmp/chain && cp -r /chain /tmp/chain"ثم ننشر العقد، ونسجل نفس حدث الإصلاح الذي سجلناه في الجدول، ونستخدم أول حساب اختبار في Anvil، ومفتاحه معروف للجميع فلا يصلح لأي شبكة حقيقية:
A="docker exec -w /tmp/chain lab-need-anvil"
K=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
$A forge create src/WarrantyLog.sol:WarrantyLog --broadcast --private-key $K
C=0x5FbDB2315678afecb367f032d93F642f64180aa3
S=$($A cast format-bytes32-string SN-1001)
$A cast send $C "record(bytes32,string)" $S "screen repaired under warranty" --private-key $K | grep -E '^(blockNumber|gasUsed|status) 'والمخرج سوف يكون كما يلي:
Compiler run successful!
Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
Deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3
Transaction hash: 0x2d510fbc1804bc706f2162e4f3318a737dc9cc485c199c3b12439d1ee12632c0
blockNumber 2
gasUsed 48159
status 1 (success)تسجيل حدث واحد استهلك 48,159 وحدة غاز، أي أكثر من ضعف التحويل البسيط الذي يكلف 21,000، والسبب أن المعاملة تكتب في تخزين العقد Storage وتصدر حدثاً Event، وكل عملية في آلة EVM لها تكلفة كما يشرح توثيق الغاز. وعلى أي شبكة حقيقية تدفع هذا الغاز بعملة الشبكة بسعر يتغير كل لحظة، لذلك نقارن بوحدات الغاز وليس بالريال.
ألف حدث في الجدول وفي العقد
الآن نكتب ألف حدث «inspected» في العقد، كل حدث في معاملة مستقلة، بسكربت Foundry. احفظ الملف التالي باسم chain/script/Load.s.sol، وهو يحتاج مكتبة forge-std التي تثبتها بالأمر forge install foundry-rs/forge-std --no-git:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {Script} from "forge-std/Script.sol";
import {WarrantyLog} from "../src/WarrantyLog.sol";
// Sends 1000 "inspected" events, one transaction each, like the 10,000 rows we wrote to PostgreSQL.
contract Load is Script {
function run(address log) external {
vm.startBroadcast();
for (uint256 i = 0; i < 1000; i++) {
WarrantyLog(log).record(bytes32(2000 + i), "inspected");
}
vm.stopBroadcast();
}
}$A forge script script/Load.s.sol:Load --sig "run(address)" $C --rpc-url http://127.0.0.1:8545 --private-key $K --broadcastوالجزء الأخير من المخرج سوف يكون كما يلي:
==========================
Chain 31337
Estimated max fee per gas: 1.758282451 gwei
Estimated base fee per gas: 0.879141225 gwei
Estimated max priority fee per gas: 0.000000001 gwei
Estimated total gas used for script: 66103480
Estimated amount required: 0.11622858883402948 ETH
==========================
==========================
ONCHAIN EXECUTION COMPLETE & SUCCESSFUL.
Transactions saved to: /tmp/chain/broadcast/Load.s.sol/31337/run-latest.json
Sensitive values saved to: /tmp/chain/cache/Load.s.sol/31337/run-latest.jsonوملف الإيصالات الذي يحفظه forge في broadcast/Load.s.sol/31337/run-latest.json يبين ما حدث فعلاً: ألف إيصال كلها ناجحة، ومجموع الغاز المستهلك فعلاً 47,846,952 وحدة، أي بين 47,835 و47,847 لكل حدث، ووزعت Anvil المعاملات على 22 كتلة من الكتلة 3 إلى الكتلة 24. ولاحظ أن رقم «Estimated total gas» تقدير أعلى من الحقيقي، فالأداة تحسب هامش أمان. والمقارنة العادلة ليست بالسرعة على جهاز واحد، فشبكة Anvil المحلية عقدة واحدة دون أي إجماع، وإنما بطبيعة التكلفة.
وفي الجهة الأخرى نكتب 10,000 حدث في جدول PostgreSQL بنفس دالة السلسلة. نولد ملف الإدراجات بسطر Python، ثم ننفذه مرتين: مرة كل إدراج في معاملة مستقلة، ومرة كلها في معاملة واحدة بالخيار -1:
python3 -c "
with open('bulk.sql','w') as f:
for i in range(10000): f.write(f\"INSERT INTO warranty_events (party, serial_no, event) VALUES ('service', 'SN-{2000+i}', 'inspected');\n\")"
time (docker exec -i lab-need-pg psql -U postgres -q -f /lab/bulk.sql)
time (docker exec -i lab-need-pg psql -U postgres -q -1 -f /lab/bulk.sql)
docker exec -i lab-need-pg psql -U postgres -f /lab/02-verify.sql
docker exec -i lab-need-pg psql -U postgres -At -c "select pg_size_pretty(pg_total_relation_size('warranty_events'))"والمخرج سوف يكون كما يلي، مع حذف سطري user وsys من كل قياس:
real 0m24.856s
real 0m5.761s
rows | bad_content | bad_links | first_bad_id
-------+-------------+-----------+--------------
20004 | 0 | 0 |
(1 row)
3408 kBفي الإعداد أعلاه لاحظ التالي:
- عشرة آلاف إدراج كل منها في معاملة مستقلة احتاجت قرابة 25 ثانية، أي قرابة 400 إدراج في الثانية، رغم القفل الذي يجعلها متسلسلة، وأغلب الوقت في تثبيت كل معاملة على القرص Commit.
- نفس العدد في معاملة واحدة احتاج أقل من 6 ثوان، وهذا خيار لا يوجد في الشبكة، فكل حدث هناك معاملة موقعة مستقلة لها رسومها.
- التحقق مر على 20,004 صفاً وكلها سليمة، أي أن السلسلة لم تتفرع رغم الإدراجات المتتالية، وهذه فائدة القفل.
- الجدول كله 3.4 ميجابايت على سيرفر واحد تملكه وتنسخه احتياطياً، أما بيانات العقد فتحفظها كل عقدة في الشبكة طول عمرها.
وعلى شبكة Ethereum العامة لا تحدد سرعة جهازك متى تدخل المعاملة، وإنما الشبكة نفسها: الزمن مقسم إلى خانات Slots مدة كل منها 12 ثانية كما في توثيق إثبات الحصة، والنهائية Finality تأتي على حدود الحقب Epochs كما شرحنا في قسم النهائية. والصورة التالية تضع الأرقام جنباً إلى جنب:

نجرب العطل: «نحذف» رقم الجوال من العقد
نكتب رقم جوال وهمياً لصاحب الجهاز بحساب ثان، ثم «نحذفه» بكتابة نص فارغ مكانه، ثم نقرأ القيمة الحالية:
K1=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d
$A cast send $C "setPhone(bytes32,string)" $S "0555123456" --private-key $K1 | grep -E '^(blockNumber|status|transactionHash) '
$A cast send $C "setPhone(bytes32,string)" $S "" --private-key $K1 | grep -E '^(blockNumber|status) '
$A cast call $C "ownerPhone(bytes32)(string)" $Sوالمخرج سوف يكون كما يلي:
blockNumber 25
status 1 (success)
transactionHash 0x93a908ce05bc2569303698e32112c560971fba81267cbd05b2ce14f3a938b1a9
blockNumber 26
status 1 (success)
""القيمة الحالية فارغة، والتطبيق الذي يقرأ الحالة الحالية سوف يقول إن الرقم حذف. ولكن ماذا يحدث إذا سأل أحد عن حالة العقد في الكتلة السابقة، أو قرأ المعاملة نفسها؟
$A cast call $C "ownerPhone(bytes32)(string)" $S --block 25
$A cast calldata-decode "setPhone(bytes32,string)" $($A cast tx 0x93a908ce05bc2569303698e32112c560971fba81267cbd05b2ce14f3a938b1a9 input)والمخرج سوف يكون كما يلي:
"0555123456"
0x534e2d3130303100000000000000000000000000000000000000000000000000
"0555123456"الرقم ما زال موجوداً في مكانين: في حالة العقد التاريخية التي تحفظها أي عقدة أرشيفية Archive Node، وفي بيانات المعاملة Calldata التي حفظتها كل عقدة كاملة لأنها جزء من الكتلة. ولا يوجد أمر في الشبكة يحذف معاملة من كتلة قديمة، لأن حذفها يغير هاش الكتلة وكل ما بعدها، وهذه هي الخاصية التي اخترت البلوكتشين من أجلها في الأصل.
ماذا لو نشرنا هاش الرقم بدلاً من الرقم؟
هذا هو الحل الذي يقترحه أغلب الفرق عند هذه النقطة: لا نضع الرقم، وإنما هاشه، والهاش لا يمكن الرجوع منه إلى المدخل. وهذا صحيح عندما يكون المدخل عشوائياً وطويلاً، أما أرقام الجوال فشكلها معروف وعددها محدود. والملف التالي يجرب ذلك، احفظه باسم phonehash.py:
# "We only put the hash of the phone number on the chain" - is that enough?
import hashlib, hmac, secrets, time
phone = "0555123456" # a made-up number
on_chain = hashlib.sha256(phone.encode()).hexdigest() # what a naive design publishes
print("published hash:", on_chain[:24], "...")
start = time.perf_counter()
for n in range(1_000_000): # every number that starts with 0555
guess = f"0555{n:06d}"
if hashlib.sha256(guess.encode()).hexdigest() == on_chain:
print(f"found {guess} after {n + 1:,} guesses in {time.perf_counter() - start:.2f}s")
break
key = secrets.token_bytes(32) # kept off-chain by the operator (a pepper)
tag = hmac.new(key, phone.encode(), hashlib.sha256).hexdigest()
print("HMAC published:", tag[:24], "... (without the key, guessing numbers gives nothing)")docker run --rm --name lab-need-py -v "$PWD:/lab" -w /lab python:3.13.16-slim python phonehash.pyوالمخرج سوف يكون كما يلي:
published hash: 89a50e2c8b02e95641cc8b4a ...
found 0555123456 after 123,457 guesses in 0.39s
HMAC published: 150224401bad18b36ca445c1 ... (without the key, guessing numbers gives nothing)وجدنا الرقم في أقل من نصف ثانية بلغة Python على جهاز عادي، ولو كان الرقم في آخر المليون لاحتاج بضع ثوان فقط. لذلك هاش البيانات الشخصية دون سر ليس إخفاء لها، وهذه نفس المشكلة التي شرحناها مع كلمات المرور والملح Salt والفلفل Pepper في مقال التشفير للمطورين. والتصميم الأنسب أن تبقى البيانات الشخصية في قاعدة بيانات تستطيع أن تصححها وتحذفها، وأن تضع في السلسلة عند الحاجة مرجعاً عشوائياً أو HMAC بمفتاح سري لا يغادر سيرفرك، فإذا طلب صاحب البيانات الإتلاف حذفت السطر، ولم يبق في السلسلة ما يدل عليه. والصورة التالية تلخص التجربتين:

السؤال الخامس: هل يجب أن يتحقق أي شخص في العالم دون إذن؟
إذا وصلت إلى هنا فعندك أطراف مستقلة لا تقبل مشغلاً واحداً، ولا يكفيها اكتشاف التعديل بعد حدوثه، وقد أبعدت البيانات الشخصية عن السلسلة. والسؤال الأخير يحدد نوع الشبكة: هل المطلوب أن يتحقق أي شخص في العالم دون أن يطلب إذناً من أحد، أم أن المشاركين مجموعة معروفة من الشركات؟
في منصة الضمان المشاركون معروفون: مصانع وموزعون ومتاجر ومراكز صيانة لها سجلات تجارية، والعميل لا يحتاج أن يشغل عقدة، وإنما يحتاج أن يثق بأن الشهادة التي يراها صحيحة. لذلك إذا وصلت المنصة إلى هذه النقطة فالمخرج هو شبكة مصرح بها Permissioned، يشغل فيها كل عضو عقدته، ويصدر لكل عضو هوية بشهادة، والإجماع فيها بالتصويت بين الأعضاء كما في Hyperledger Fabric أو Besu، وسوف نبني شبكة من هذا النوع في مقال قادم عن البلوكتشين الخاص للمؤسسات. ولاحظ أن هذه الشبكة لها معنى فقط إذا شغل الأعضاء عقدهم فعلاً، فإذا شغلت شركتك كل العقد نيابة عنهم عدت إلى قاعدة بيانات منسوخة أبطأ وأغلى، وهذه ملاحظة سوف تراها في الحالة الثانية أدناه.
أما الشبكة العامة فمكانها عندما يكون التحقق العلني هو المنتج نفسه: أصل رقمي يملكه أي شخص وينقله دون إذن، أو سجل يجب أن يبقى قابلاً للتحقق حتى لو اختفت كل الشركات المشاركة. وهنا تقبل كل الأثمان معاً: البيانات علنية للأبد، ولكل كتابة رسوم، والمفتاح الضائع لا يعيده أحد، والخصوصية تحتاج تصميماً خاصاً.
حالتان موثقتان: حين لم تجد الأسئلة جواباً
التقنية في الحالتين التاليتين عملت، والذي أوقف المشروعين كان في الأسئلة الأولى، وهذا هو الدرس الذي يهمك وأنت تكتب رد الأسبوع للمدير التنفيذي.
الحالة الأولى منصة TradeLens لتتبع الشحن، والتي أعلنت عنها Maersk وIBM سنة 2018 كمنصة مبنية على البلوكتشين. وفي 29 نوفمبر 2022 أعلنت Maersk في بيانها الصحفي قرار إيقاف المنصة وإغلاقها بنهاية الربع الأول من 2023، ونقلت عن مسؤول منصات الأعمال فيها أنهم طوروا منصة قابلة للعمل، ولكن التعاون الكامل بين شركات القطاع على مستوى العالم لم يتحقق، فلم تصل المنصة إلى مستوى الجدوى التجارية اللازم للاستمرار. وإذا قرأت ذلك بأسئلتنا فهو السؤالان الأول والثاني معاً: المنصة تحتاج كتاباً مستقلين كثيرين من شركات شحن متنافسة، وهذه الشركات تحتاج أن تقبل منصة تقف خلفها شركة منافسة، والتقنية لم تحل هذه المشكلة.
والحالة الثانية مشروع استبدال نظام CHESS للمقاصة والتسوية في بورصة أستراليا ASX. ففي 7 ديسمبر 2017 أعلنت ASX في بيان رسمي أنها ستستبدل CHESS بتقنية السجل الموزع DLT من شريكها Digital Asset، وأن النظام سوف تشغله ASX على شبكة خاصة آمنة المشاركون فيها معروفون ومصرح لهم. ثم في 17 نوفمبر 2022 أعلنت في بيان آخر أنها أوقفت العمل الحالي في المشروع بعد مراجعة مستقلة من Accenture قدرت أن التطبيق مكتمل بنسبة 63% فقط، وذكرت تعقيداً في التصميم المتكامل منه طريقة تفاعل متطلبات ASX مع التطبيق والسجل الذي تحته، وأنها سوف تشطب برمجيات مرسملة بقيمة 245 إلى 255 مليون دولار أسترالي قبل الضريبة. وكما يذكر بيان مارس 2024 أعلنت ASX في نوفمبر 2023 أنها ستمضي في تصميم قائم على منتج جاهز تقدمه TATA Consultancy Services. وبأسئلتنا فهذه حالة السؤال الثاني: هناك جهة واحدة يقبلها السوق وتشغل النظام أصلاً، وهي ASX نفسها، فالسجل الموزع لم يحل مشكلة ثقة غير موجودة، وأضاف تعقيداً في التصميم.

ماذا تقول أطر القرار المعروفة؟
الأسئلة الخمسة ليست اختراعاً جديداً، وإنما ترتيب عملي لما تقوله الأطر المعروفة. فتقرير NIST IR 8202 في القسم 8 يذكر أن البلوكتشين قد يناسب الأنظمة التي فيها مشاركون كثيرون وموزعون، ولا يوجد فيها طرف ثالث موثوق أو لا يراد وجوده، والعمل فيها معاملات تنقل أصولاً أو معلومات بين الأطراف، ويحتاج الجميع تاريخاً كاملاً مشتركاً، ثم يعرض مخطط قرار من مديرية العلوم والتقنية في وزارة الأمن الداخلي الأمريكية DHS. ويشير التقرير أيضاً إلى ورقة Do you need a Blockchain? لـ Karl Wüst وArthur Gervais من ETH Zurich، ويلاحظ أن أغلب المسارات في مخططها تنتهي بالإجابة «لا»، ويقول إن هذه النظرة النقدية هي التي يجب أن تأخذها أغلب المؤسسات.
ومخطط الورقة يسأل بالترتيب: هل تحتاج تخزين حالة State أصلاً؟ ثم هل هناك أكثر من كاتب؟ ثم هل تستطيع استخدام طرف ثالث موثوق متصل دائماً؟ ثم هل كل الكتاب معروفون؟ ثم هل يثق الكتاب ببعضهم؟ ثم هل تحتاج تحققاً علنياً؟ وتنتهي إجاباته إلى «لا تحتاج بلوكتشين» أو شبكة عامة أو شبكة مصرح بها علنية أو خاصة. والفرق بينه وبين أسئلتنا أننا أضفنا سؤالين يظهران كثيراً في المشاريع الحقيقية: سؤال إظهار التعديل بنقاط تحقق موقعة، لأنه البديل الذي يحل أغلب طلبات «لا أريد أن يغير أحد البيانات»، وسؤال البيانات الشخصية، لأنه يغير التصميم كله في أي نظام يخدم أفراداً في المملكة.
أخطاء شائعة وكيف تلاحظها
- «البلوكتشين يضمن صحة البيانات»: السلسلة تضمن أن ما كتب لم يتغير، ولا تضمن أن ما كتب صحيح. فإذا سجل مركز الصيانة إصلاحاً لم يحدث فالسلسلة سوف تحفظ الكذبة للأبد بكل أمانة، ولهذا السبب يسمي الناس مشكلة إدخال الواقع إلى السلسلة مشكلة الأوراكل Oracle Problem. لاحظها عندما يقول أحد «البيانات على البلوكتشين إذن هي موثوقة».
- اتحاد من عضو واحد: شبكة «خاصة» تشغل كل عقدها شركة واحدة، فهي قاعدة بيانات منسوخة أبطأ، لأن الشركة تستطيع أن تعيد بناء السلسلة متى شاءت. لاحظها بسؤال واحد: «من يملك السيرفرات التي تعمل عليها العقد؟».
- نسيان التصحيح: الأخطاء البشرية في الإدخال تحدث كل يوم، وفي قاعدة البيانات تصلحها بتعديل وسجل تدقيق، وفي السلسلة تحتاج معاملة تصحيح جديدة يفهمها كل من يقرأ، فصمم ذلك من اليوم الأول.
- هاش البيانات الشخصية دون سر: كما رأينا في التجربة، رقم الجوال يعود من هاشه في أقل من ثانية.
- حساب التكلفة بسعر اليوم: الغاز يدفع بعملة سعرها يتغير، فالرسوم التي تبدو صغيرة اليوم قد تتضاعف في أسبوع مزدحم، لذلك ضع الميزانية بوحدات الغاز وحدودها، وليس بالريال.
- تجاهل تكلفة المفاتيح: كل كاتب يحتاج مفتاحاً خاصاً يحميه ويدير تدويره، ومفتاح مسرب في شبكة يعني كتابات باسمك لا تستطيع حذفها.
ورقة القرار: من الأرخص إلى الأغلى
الصورة التالية تبين المخارج الأربعة للاختبار، مرتبة من الأرخص إلى الأغلى:

والجدول التالي هو الورقة التي تملؤها مع فريقك، وفيه إجابات منصة الضمان كمثال:
| السؤال | كيف تتحقق من الإجابة | إجابة منصة الضمان | النتيجة |
|---|---|---|---|
| 1. عدة كتاب مستقلين؟ | هل يكتب كل طرف بمفتاحه وعقدته، أم عبر واجهتك؟ | اليوم عبر واجهتنا | كاتب واحد، ولكن الموزع يريد ضماناً، فنكمل |
| 2. جهة يقبلها الجميع؟ | اسأل الشركاء مباشرة، واقرأ العقود | شركتنا محايدة ولا تبيع أجهزة | نعم بشرط ضمان إضافي للموزع |
| 3. يكفي إظهار التعديل؟ | هل يمكن حل الخلاف بالعقود بعد اكتشافه؟ | نعم، الخلاف على ضمان جهاز يحل تجارياً | سجل بسلسلة هاش ونقاط تحقق يومية موقعة |
| 4. بيانات شخصية؟ | هل فيها اسم أو جوال أو عنوان أو هوية؟ | نعم، بيانات العميل | في قاعدة البيانات فقط، قابلة للتصحيح والإتلاف |
| 5. تحقق علني دون إذن؟ | هل يحتاج غير المشاركين أن يتحققوا بأنفسهم؟ | لا | لم نصل إليه |
وبهذا الشكل يكون رد الأسبوع للمدير التنفيذي جملة واحدة: لا نحتاج بلوكتشين الآن، وسوف نعطي الموزع ضماناً أقوى مما طلب بسلسلة هاش ونقطة تحقق موقعة يحفظها عنده كل يوم، بتكلفة أيام عمل قليلة بدلاً من شبكة كاملة، وإذا تغيرت الإجابة عن السؤال الثاني يوماً ما، كأن ينضم منافس لا يقبل أن نشغل السجل، نعيد الاختبار.
قائمة تحقق قبل أي قرار
- اكتب المتطلب بلغة العمل وليس بلغة التقنية: «لا يغير أحد الشهادات دون أن نعرف» وليس «نريد بلوكتشين».
- عد الكتاب المستقلين فعلاً، أي من يكتب بمفتاحه وعقدته، وليس عدد المستخدمين.
- اسأل الشركاء بالاسم عن الجهة التي يقبلون أن تشغل السجل، واكتب إجاباتهم.
- جرب سجل سلسلة الهاش مع نقاط التحقق الموقعة قبل أي شبكة، فهو أيام عمل وليس أشهراً.
- أخرج كل بيانات شخصية من أي تصميم سلسلة، ولا تعتمد على الهاش وحده لإخفائها.
- إذا وصلت إلى شبكة مصرح بها، فتأكد أن كل عضو سوف يشغل عقدته بنفسه، واكتب الحوكمة: من يضيف عضواً، ومن يقرر الترقيات، وماذا يحدث عند خلاف.
- قدر التكلفة بوحدات الغاز أو بعدد العقد والسيرفرات، وليس بسعر العملة اليوم.
- راجع المستشار القانوني في البيانات الشخصية وفي أي أصل رقمي قابل للتحويل قبل كتابة الكود.
إزالة التجربة
docker rm -f -v lab-need-pg lab-need-anvil
docker image rm postgres:18.6-alpine ghcr.io/foundry-rs/foundry:v1.8.6تدريب عملي
- عدل الصف 2 بدلاً من الصف 4، ثم اكتب استعلاماً يعيد حساب هاش كل الصفوف من الصف 2 حتى الآخر بالترتيب، وتأكد أن التحقق الداخلي يمر وأن نقطة التحقق تكشف التعديل.
- أضف إلى
checkpoint.pyأمراً ثالثاً يتحقق من أن الصف الذي يخص شريكاً معيناً جزء من السلسلة، بإعطائه الصف وهاشات الصفوف بعده حتى نقطة التحقق. - غير
phonehash.pyليستخدم HMAC ثم حاول التخمين بنفس الحلقة، وفسر لماذا لا تجد شيئاً.
الخلاصة
وصلنا لنهاية الموضوع، وأهم ما فيه:
- البلوكتشين يبيعك ثلاثة ضمانات: لا مشغل يغير الماضي وحده، ولا مشغل يرفض معاملة صحيحة وحده، وتحقق مستقل عند كل طرف، وثمنها ثابت في الأداء والخصوصية والحذف والمفاتيح والحوكمة.
- السؤال الأول عن الكتاب المستقلين فعلاً، والثاني عن جهة يقبلها الجميع، وأغلب المشاريع تنتهي عندهما بقاعدة بيانات.
- السؤال الثالث هو البديل الأقوى: سلسلة هاش في PostgreSQL مع نقاط تحقق موقعة عند الشركاء كشفت في التجربة إعادة الكتابة الكاملة التي لم يكشفها التحقق الداخلي.
- ما يدخل السلسلة لا يخرج منها: الرقم «المحذوف» بقي في الحالة القديمة وفي بيانات المعاملة، وهاش رقم الجوال عاد إلى الرقم في أقل من نصف ثانية.
- تسجيل حدث واحد في العقد كلف 48,159 غاز، وكل كتابة في الشبكة معاملة مستقلة تنتظر كتلة وتحفظها كل عقدة، أما الجدول فكتب عشرة آلاف حدث في ثوان على سيرفر واحد.
- TradeLens وCHESS عملت فيهما التقنية، وتعثرا في أسئلة من يشارك ومن يشغل وكم يكلف.
دليلكيف يعمل البلوكتشين Blockchain ومتى لا تحتاجهما المشكلة التي يحلها البلوكتشين أصلاً وكيف يعمل من الداخل، ومتى تكون قاعدة البيانات العادية هي الحل الأصح.
دليلكيف تتفق شبكة البلوكتشين؟ إثبات العمل وإثبات الحصةكيف تتفق آلاف العقد التي لا يثق بعضها ببعض على سجل واحد، ولماذا يكلف الغش كهرباء في شبكة ومالاً محجوزاً في أخرى.
دليلAudit Trail: كيف تبني سجل تدقيق لا يقبل العبثكيف تصمم سجلاً يجيب عن سؤال من غير هذه الصلاحية ومتى، وتحميه من الحذف والتعديل بعد كتابته.
دليلتاريخ العملات الرقمية: من Cypherpunks إلى Bitcoinكيف حاول المهندسون صنع نقد رقمي أربعين سنة، ولماذا تعثرت المحاولات قبل Bitcoin، وما الذي جمعه Bitcoin منها.
دليلالتشفير في البلوكتشين: الهاش والمنحنيات والتوقيعاتكيف تصنع المفاتيح والعناوين والتوقيعات في Bitcoin وEthereum، ولماذا يكشف رقم مكرر واحد في التوقيع المفتاح الخاص.
دليلشبكة بلوكتشين خاصة للمؤسسات: Besu أم Fabric؟كيف تبني شبكة خاصة من أربع شركات بإجماع QBFT، وماذا يحدث عندما يتوقف أعضاؤها، ومتى تختار Fabric أو قاعدة بيانات.سجل التحديثات
- أكتوبر 2026: كتابة المقال، واختبار التجارب على Docker Engine 29.8.0 مع PostgreSQL 18.6 وPython 3.13.16 ومكتبة cryptography 46.0.3، وFoundry v1.8.6 مع Solc 0.8.30 وforge-std v1.17.0.