মার্কেল প্যাট্রিসিয়া ট্রাই
পেজ সর্বশেষ আপডেট করা হয়েছে: 25 ফেব্রুয়ারী, 2026
Ethereum-এর স্টেট (সমস্ত একাউন্ট, ব্যালেন্স এবং স্মার্ট কন্ট্রাক্টের সমষ্টি) একটি বিশেষ সংস্করণের ডেটা স্ট্রাকচারে এনকোড করা হয় যা কম্পিউটার বিজ্ঞানে সাধারণত মার্কেল ট্রি (Merkle Tree) নামে পরিচিত। এই স্ট্রাকচারটি ক্রিপ্টোগ্রাফির অনেক অ্যাপ্লিকেশনের জন্য দরকারী কারণ এটি ট্রিতে থাকা সমস্ত পৃথক ডেটার মধ্যে একটি যাচাইযোগ্য সম্পর্ক তৈরি করে, যার ফলে একটি একক root ভ্যালু তৈরি হয় যা ডেটা সম্পর্কে বিভিন্ন বিষয় প্রমাণ করতে ব্যবহার করা যেতে পারে।
ইথেরিয়ামের ডেটা স্ট্রাকচার হলো একটি 'মডিফাইড মার্কেল-প্যাট্রিসিয়া ট্রাই', এর নামকরণ এমন করা হয়েছে কারণ এটি PATRICIA (the Practical Algorithm To Retrieve Information Coded in Alphanumeric)-এর কিছু বৈশিষ্ট্য ধার করে এবং এটি ইথেরিয়াম স্টেট গঠনকারী আইটেমগুলোর দক্ষ ডেটা রিট্রাইভাল (retrieval)-এর জন্য ডিজাইন করা হয়েছে।
একটি মার্কেল-প্যাট্রিসিয়া ট্রাই ডিটারমিনিস্টিক এবং ক্রিপ্টোগ্রাফিকভাবে যাচাইযোগ্য: একটি স্টেট রুট তৈরি করার একমাত্র উপায় হলো স্টেটের প্রতিটি পৃথক অংশ থেকে এটি গণনা করা, এবং দুটি অভিন্ন স্টেট সহজেই রুট হ্যাস এবং এর সাথে যুক্ত হ্যাসগুলোর তুলনা করে প্রমাণ করা যায় (একটি মার্কেল প্রুফ)। বিপরীতভাবে, একই রুট হ্যাস দিয়ে দুটি ভিন্ন স্টেট তৈরি করার কোনো উপায় নেই, এবং ভিন্ন ভ্যালু দিয়ে স্টেট পরিবর্তন করার যেকোনো প্রচেষ্টার ফলে একটি ভিন্ন স্টেট রুট হ্যাস তৈরি হবে। তাত্ত্বিকভাবে, এই স্ট্রাকচারটি ইনসার্ট, লুকআপ এবং ডিলিট করার জন্য O(log(n)) দক্ষতার 'হোলি গ্রেইল' প্রদান করে।
নিকট ভবিষ্যতে, ইথেরিয়াম একটি ভার্কেল ট্রি (Verkle Tree) স্ট্রাকচারে স্থানান্তরিত হওয়ার পরিকল্পনা করছে, যা ভবিষ্যতের প্রটোকল উন্নতির জন্য অনেক নতুন সম্ভাবনা উন্মুক্ত করবে।
পূর্বশর্ত
এই পেজটি ভালোভাবে বোঝার জন্য, হ্যাস (opens in a new tab), মার্কেল ট্রি (opens in a new tab), ট্রাই (opens in a new tab) এবং সিরিয়ালাইজেশন (opens in a new tab) সম্পর্কে প্রাথমিক জ্ঞান থাকা সহায়ক হবে। এই আর্টিকেলটি একটি বেসিক র্যাডিক্স ট্রি (radix tree) (opens in a new tab)-এর বর্ণনা দিয়ে শুরু হয়, তারপর ধীরে ধীরে ইথেরিয়ামের আরও অপ্টিমাইজ করা ডেটা স্ট্রাকচারের জন্য প্রয়োজনীয় পরিবর্তনগুলো তুলে ধরে।
বেসিক র্যাডিক্স ট্রাই
একটি বেসিক র্যাডিক্স ট্রাই-তে, প্রতিটি নোড দেখতে নিচের মতো হয়:
[i_0, i_1 ... i_n, value]
যেখানে i_0 ... i_n বর্ণমালার প্রতীকগুলোকে (প্রায়শই বাইনারি বা হেক্স) উপস্থাপন করে, value হলো নোডের টার্মিনাল ভ্যালু, এবং i_0, i_1 ... i_n স্লটগুলোর ভ্যালু হয় NULL অথবা অন্যান্য নোডের পয়েন্টার (আমাদের ক্ষেত্রে, হ্যাস)। এটি একটি বেসিক (key, value) স্টোর তৈরি করে।
ধরা যাক আপনি কী-ভ্যালু (key-value) পেয়ারের একটি সেটের উপর একটি অর্ডার বজায় রাখার জন্য একটি র্যাডিক্স ট্রি ডেটা স্ট্রাকচার ব্যবহার করতে চান। ট্রাই-তে বর্তমানে dog কী-এর সাথে ম্যাপ করা ভ্যালুটি খুঁজে পেতে, আপনাকে প্রথমে dog-কে বর্ণমালার অক্ষরে রূপান্তর করতে হবে (যা 64 6f 67 দেয়), এবং তারপর ভ্যালুটি খুঁজে না পাওয়া পর্যন্ত সেই পথ অনুসরণ করে ট্রাই-এর নিচে নামতে হবে। অর্থাৎ, আপনি ট্রাই-এর রুট নোড খুঁজে পেতে একটি ফ্ল্যাট কী/ভ্যালু ডিবি (DB)-তে রুট হ্যাস খোঁজার মাধ্যমে শুরু করবেন। এটি অন্যান্য নোডের দিকে নির্দেশকারী কী-গুলোর একটি অ্যারে হিসেবে উপস্থাপিত হয়। আপনি ইনডেক্স 6-এর ভ্যালুটিকে একটি কী হিসেবে ব্যবহার করবেন এবং এক স্তর নিচের নোডটি পেতে ফ্ল্যাট কী/ভ্যালু ডিবি-তে এটি খুঁজবেন। তারপর পরবর্তী ভ্যালুটি খুঁজতে ইনডেক্স 4 বেছে নিন, তারপর ইনডেক্স 6 বেছে নিন, এবং এভাবেই চলতে থাকবে, যতক্ষণ না আপনি পথটি অনুসরণ করেন: root -> 6 -> 4 -> 6 -> 15 -> 6 -> 7, আপনি নোডের ভ্যালুটি খুঁজবেন এবং ফলাফলটি রিটার্ন করবেন।
'ট্রাই' এবং অন্তর্নিহিত ফ্ল্যাট কী/ভ্যালু 'ডিবি'-তে কিছু খোঁজার মধ্যে পার্থক্য রয়েছে। তারা উভয়ই কী/ভ্যালু বিন্যাস সংজ্ঞায়িত করে, কিন্তু অন্তর্নিহিত ডিবি একটি কী-এর ঐতিহ্যবাহী 1 ধাপের লুকআপ করতে পারে। ট্রাই-তে একটি কী খোঁজার জন্য উপরে বর্ণিত চূড়ান্ত ভ্যালুতে পৌঁছানোর জন্য একাধিক অন্তর্নিহিত ডিবি লুকআপের প্রয়োজন হয়। অস্পষ্টতা দূর করতে চলুন পরেরটিকে একটি path হিসেবে উল্লেখ করি।
র্যাডিক্স ট্রাই-এর জন্য আপডেট এবং ডিলিট অপারেশনগুলো নিম্নরূপ সংজ্ঞায়িত করা যেতে পারে:
def update(node_hash, path, value):
curnode = db.get(node_hash) if node_hash else [NULL] * 17
newnode = curnode.copy()
if path == "":
newnode[-1] = value
else:
newindex = update(curnode[path[0]], path[1:], value)
newnode[path[0]] = newindex
db.put(hash(newnode), newnode)
return hash(newnode)
def delete(node_hash, path):
if node_hash is NULL:
return NULL
else:
curnode = db.get(node_hash)
newnode = curnode.copy()
if path == "":
newnode[-1] = NULL
else:
newindex = delete(curnode[path[0]], path[1:])
newnode[path[0]] = newindex
if all(x is NULL for x in newnode):
return NULL
else:
db.put(hash(newnode), newnode)
return hash(newnode)
একটি "মার্কেল" র্যাডিক্স ট্রি ডিটারমিনিস্টিকভাবে তৈরি ক্রিপ্টোগ্রাফিক হ্যাস ডাইজেস্ট ব্যবহার করে নোডগুলোকে যুক্ত করে তৈরি করা হয়। এই কন্টেন্ট-অ্যাড্রেসিং (কী/ভ্যালু ডিবি-তে key == keccak256(rlp(value))) সংরক্ষিত ডেটার একটি ক্রিপ্টোগ্রাফিক অখণ্ডতার গ্যারান্টি প্রদান করে। যদি একটি নির্দিষ্ট ট্রাই-এর রুট হ্যাস সর্বজনীনভাবে পরিচিত হয়, তবে অন্তর্নিহিত লিফ (leaf) ডেটাতে অ্যাক্সেস থাকা যে কেউ প্রমাণ তৈরি করতে পারে যে ট্রাই-টি একটি নির্দিষ্ট পথে একটি নির্দিষ্ট ভ্যালু অন্তর্ভুক্ত করে, যা ট্রি রুটের সাথে একটি নির্দিষ্ট ভ্যালু যুক্ত করা প্রতিটি নোডের হ্যাস প্রদান করে করা যায়।
একজন আক্রমণকারীর পক্ষে এমন একটি (path, value) পেয়ারের প্রমাণ দেওয়া অসম্ভব যা বিদ্যমান নেই কারণ রুট হ্যাস শেষ পর্যন্ত এর নিচের সমস্ত হ্যাসের উপর ভিত্তি করে তৈরি। যেকোনো অন্তর্নিহিত পরিবর্তন রুট হ্যাস পরিবর্তন করবে। আপনি হ্যাসটিকে ডেটা সম্পর্কে কাঠামোগত তথ্যের একটি সংকুচিত উপস্থাপনা হিসেবে ভাবতে পারেন, যা হ্যাসিং ফাংশনের প্রি-ইমেজ সুরক্ষা দ্বারা সুরক্ষিত।
আমরা একটি র্যাডিক্স ট্রির একটি পারমাণবিক একককে (যেমন, একটি একক হেক্স ক্যারেক্টার, বা 4 বিট বাইনারি সংখ্যা) "নিবল (nibble)" হিসেবে উল্লেখ করব। উপরে বর্ণিত হিসেবে, একবারে একটি নিবল পথ অতিক্রম করার সময়, নোডগুলো সর্বাধিক 16টি চিলড্রেনকে নির্দেশ করতে পারে তবে একটি value উপাদান অন্তর্ভুক্ত করে। তাই, আমরা সেগুলোকে 17 দৈর্ঘ্যের একটি অ্যারে হিসেবে উপস্থাপন করি। আমরা এই 17-উপাদানের অ্যারেগুলোকে "ব্রাঞ্চ নোড (branch nodes)" বলি।
মার্কেল প্যাট্রিসিয়া ট্রাই
র্যাডিক্স ট্রাই-এর একটি প্রধান সীমাবদ্ধতা রয়েছে: এগুলো অদক্ষ। আপনি যদি একটি (path, value) বাইন্ডিং সংরক্ষণ করতে চান যেখানে পথটি, ইথেরিয়ামের মতো, 64 ক্যারেক্টার দীর্ঘ (bytes32-এ নিবলের সংখ্যা), তবে প্রতি ক্যারেক্টারে একটি স্তর সংরক্ষণ করতে আমাদের এক কিলোবাইটের বেশি অতিরিক্ত জায়গার প্রয়োজন হবে, এবং প্রতিটি লুকআপ বা ডিলিট করতে পুরো 64টি ধাপ লাগবে। নিচে প্রবর্তিত প্যাট্রিসিয়া ট্রাই এই সমস্যার সমাধান করে।
অপ্টিমাইজেশন
একটি মার্কেল প্যাট্রিসিয়া ট্রাই-এর একটি নোড নিচের যেকোনো একটি হতে পারে:
NULL(খালি স্ট্রিং হিসেবে উপস্থাপিত)branchএকটি 17-আইটেমের নোড[ v0 ... v15, vt ]leafএকটি 2-আইটেমের নোড[ encodedPath, value ]extensionএকটি 2-আইটেমের নোড[ encodedPath, key ]
64 ক্যারেক্টারের পথের সাথে এটি অনিবার্য যে ট্রাই-এর প্রথম কয়েকটি স্তর অতিক্রম করার পরে, আপনি এমন একটি নোডে পৌঁছাবেন যেখানে নিচের দিকের অন্তত কিছু অংশের জন্য কোনো ভিন্ন পথ বিদ্যমান নেই। পথ বরাবর 15টি পর্যন্ত স্পার্স NULL নোড তৈরি করা এড়াতে, আমরা [ encodedPath, key ] ফর্মের একটি extension নোড সেট আপ করে নামার পথটি শর্টকাট করি, যেখানে encodedPath-এ এগিয়ে যাওয়ার জন্য "আংশিক পথ" থাকে (নিচে বর্ণিত একটি কমপ্যাক্ট এনকোডিং ব্যবহার করে), এবং key হলো পরবর্তী ডিবি লুকআপের জন্য।
একটি leaf নোডের জন্য, যা encodedPath-এর প্রথম নিবলে একটি ফ্ল্যাগ দ্বারা চিহ্নিত করা যেতে পারে, পথটি পূর্ববর্তী সমস্ত নোডের পথের অংশগুলোকে এনকোড করে এবং আমরা সরাসরি value খুঁজতে পারি।
তবে, উপরের এই অপ্টিমাইজেশনটি অস্পষ্টতা তৈরি করে।
নিবলে পথ অতিক্রম করার সময়, আমাদের অতিক্রম করার জন্য বিজোড় সংখ্যক নিবল থাকতে পারে, কিন্তু যেহেতু সমস্ত ডেটা bytes ফরম্যাটে সংরক্ষিত থাকে। উদাহরণস্বরূপ, নিবল 1 এবং নিবল 01-এর মধ্যে পার্থক্য করা সম্ভব নয় (উভয়কেই <01> হিসেবে সংরক্ষণ করতে হবে)। বিজোড় দৈর্ঘ্য নির্দিষ্ট করতে, আংশিক পথের আগে একটি ফ্ল্যাগ যুক্ত করা হয়।
স্পেসিফিকেশন: ঐচ্ছিক টার্মিনেটর সহ হেক্স সিকোয়েন্সের কমপ্যাক্ট এনকোডিং
উপরে বর্ণিত বিজোড় বনাম জোড় অবশিষ্ট আংশিক পথের দৈর্ঘ্য এবং লিফ বনাম এক্সটেনশন নোড উভয়ের ফ্ল্যাগিং যেকোনো 2-আইটেম নোডের আংশিক পথের প্রথম নিবলে থাকে। এর ফলে নিচের ফলাফল পাওয়া যায়:
| হেক্স ক্যারেক্টার | বিটস | নোড টাইপ আংশিক | পথের দৈর্ঘ্য |
|---|---|---|---|
| 0 | 0000 | এক্সটেনশন | জোড় |
| 1 | 0001 | এক্সটেনশন | বিজোড় |
| 2 | 0010 | টার্মিনেটিং (লিফ) | জোড় |
| 3 | 0011 | টার্মিনেটিং (লিফ) | বিজোড় |
জোড় অবশিষ্ট পথের দৈর্ঘ্যের (0 বা 2) জন্য, সর্বদা আরেকটি 0 "প্যাডিং" নিবল অনুসরণ করবে।
def compact_encode(hexarray):
term = 1 if hexarray[-1] == 16 else 0
if term:
hexarray = hexarray[:-1]
oddlen = len(hexarray) % 2
flags = 2 * term + oddlen
if oddlen:
hexarray = [flags] + hexarray
else:
hexarray = [flags] + [0] + hexarray
# হেক্সঅ্যারের এখন একটি জোড় দৈর্ঘ্য রয়েছে যার প্রথম নিবলটি হলো ফ্ল্যাগস।
o = ""
for i in range(0, len(hexarray), 2):
o += chr(16 * hexarray[i] + hexarray[i + 1])
return o
উদাহরণ:
> [1, 2, 3, 4, 5, ...]
'11 23 45'
> [0, 1, 2, 3, 4, 5, ...]
'00 01 23 45'
> [0, f, 1, c, b, 8, 10]
'20 0f 1c b8'
> [f, 1, c, b, 8, 10]
'3f 1c b8'
মার্কেল প্যাট্রিসিয়া ট্রাই-তে একটি নোড পাওয়ার জন্য বর্ধিত কোড নিচে দেওয়া হলো:
def get_helper(node_hash, path):
if path == []:
return node_hash
if node_hash == "":
return ""
curnode = rlp.decode(node_hash if len(node_hash) < 32 else db.get(node_hash))
if len(curnode) == 2:
(k2, v2) = curnode
k2 = compact_decode(k2)
if k2 == path[: len(k2)]:
return get(v2, path[len(k2) :])
else:
return ""
elif len(curnode) == 17:
return get_helper(curnode[path[0]], path[1:])
def get(node_hash, path):
path2 = []
for i in range(len(path)):
path2.push(int(ord(path[i]) / 16))
path2.push(ord(path[i]) % 16)
path2.push(16)
return get_helper(node_hash, path2)
উদাহরণ ট্রাই
ধরা যাক আমরা এমন একটি ট্রাই চাই যাতে চারটি পথ/ভ্যালু পেয়ার রয়েছে ('do', 'verb'), ('dog', 'puppy'), ('doge', 'coins'), ('horse', 'stallion')।
প্রথমে, আমরা পথ এবং ভ্যালু উভয়কেই bytes-এ রূপান্তর করি। নিচে, পথগুলোর প্রকৃত বাইট উপস্থাপনা <> দ্বারা চিহ্নিত করা হয়েছে, যদিও সহজে বোঝার জন্য ভ্যালুগুলো এখনও স্ট্রিং হিসেবে দেখানো হয়েছে, যা '' দ্বারা চিহ্নিত করা হয়েছে (এগুলোও আসলে bytes হবে):
<64 6f> : 'verb'
<64 6f 67> : 'puppy'
<64 6f 67 65> : 'coins'
<68 6f 72 73 65> : 'stallion'
এখন, আমরা অন্তর্নিহিত ডিবি-তে নিচের কী/ভ্যালু পেয়ারগুলো দিয়ে এমন একটি ট্রাই তৈরি করি:
rootHash: [ <16>, hashA ]
hashA: [ <>, <>, <>, <>, hashB, <>, <>, <>, [ <20 6f 72 73 65>, 'stallion' ], <>, <>, <>, <>, <>, <>, <>, <> ]
hashB: [ <00 6f>, hashC ]
hashC: [ <>, <>, <>, <>, <>, <>, hashD, <>, <>, <>, <>, <>, <>, <>, <>, <>, 'verb' ]
hashD: [ <17>, [ <>, <>, <>, <>, <>, <>, [ <35>, 'coins' ], <>, <>, <>, <>, <>, <>, <>, <>, <>, 'puppy' ] ]
যখন একটি নোড অন্য নোডের ভিতরে রেফারেন্স করা হয়, তখন যা অন্তর্ভুক্ত করা হয় তা হলো keccak256(rlp.encode(node)), যদি len(rlp.encode(node)) >= 32 হয় অন্যথায় node যেখানে rlp.encode হলো RLP এনকোডিং ফাংশন।
মনে রাখবেন যে একটি ট্রাই আপডেট করার সময়, একটি স্থায়ী লুকআপ টেবিলে কী/ভ্যালু পেয়ার (keccak256(x), x) সংরক্ষণ করতে হবে যদি নতুন তৈরি করা নোডের দৈর্ঘ্য >= 32 হয়। তবে, যদি নোডটি এর চেয়ে ছোট হয়, তবে কিছু সংরক্ষণ করার প্রয়োজন নেই, কারণ ফাংশন f(x) = x রিভার্সিবল।
ইথেরিয়ামে ট্রাই
ইথেরিয়ামের এক্সিকিউশন লেয়ার-এর সমস্ত মার্কেল ট্রাই একটি মার্কেল প্যাট্রিসিয়া ট্রাই ব্যবহার করে।
একটি ব্লক হেডার থেকে এই ট্রাইগুলোর 3টি থেকে 3টি রুট রয়েছে।
- stateRoot
- transactionsRoot
- receiptsRoot
স্টেট ট্রাই
একটি গ্লোবাল স্টেট ট্রাই রয়েছে, এবং প্রতিবার যখন কোনো ক্লায়েন্ট একটি ব্লক প্রসেস করে তখন এটি আপডেট হয়। এতে, একটি path সর্বদা: keccak256(ethereumAddress) এবং একটি value সর্বদা: rlp(ethereumAccount)। আরও নির্দিষ্টভাবে একটি ইথেরিয়াম account হলো [nonce,balance,storageRoot,codeHash]-এর একটি 4 আইটেমের অ্যারে। এই পর্যায়ে, এটি লক্ষণীয় যে এই storageRoot হলো আরেকটি প্যাট্রিসিয়া ট্রাই-এর রুট:
স্টোরেজ ট্রাই
স্টোরেজ ট্রাই হলো যেখানে সমস্ত কন্ট্রাক্ট ডেটা থাকে। প্রতিটি একাউন্টের জন্য একটি আলাদা স্টোরেজ ট্রাই রয়েছে। একটি নির্দিষ্ট এডড্রেস-এ নির্দিষ্ট স্টোরেজ পজিশনে ভ্যালুগুলো পুনরুদ্ধার করতে স্টোরেজ এডড্রেস, স্টোরেজে সংরক্ষিত ডেটার ইন্টিজার পজিশন এবং ব্লক আইডি প্রয়োজন। এগুলোকে তারপর JSON-RPC API-তে সংজ্ঞায়িত eth_getStorageAt-এ আর্গুমেন্ট হিসেবে পাস করা যেতে পারে, যেমন, 0x295a70b2de5e3953354a6a8344e616ed314d7251 এডড্রেস-এর জন্য স্টোরেজ স্লট 0-তে ডেটা পুনরুদ্ধার করতে:
curl -X POST --data '{"jsonrpc":"2.0", "method": "eth_getStorageAt", "params": ["0x295a70b2de5e3953354a6a8344e616ed314d7251", "0x0", "latest"], "id": 1}' localhost:8545
{"jsonrpc":"2.0","id":1,"result":"0x00000000000000000000000000000000000000000000000000000000000004d2"}
স্টোরেজে অন্যান্য উপাদানগুলো পুনরুদ্ধার করা কিছুটা বেশি জটিল কারণ স্টোরেজ ট্রাই-তে পজিশনটি প্রথমে গণনা করতে হবে। পজিশনটি এডড্রেস এবং স্টোরেজ পজিশনের keccak256 হ্যাস হিসেবে গণনা করা হয়, উভয়ই 32 বাইট দৈর্ঘ্যের জন্য শূন্য দিয়ে বাম-প্যাড করা হয়। উদাহরণস্বরূপ, 0x391694e7e0b0cce554cb130d723a9d27458f9298 এডড্রেস-এর জন্য স্টোরেজ স্লট 1-এ ডেটার পজিশন হলো:
keccak256(decodeHex("000000000000000000000000391694e7e0b0cce554cb130d723a9d27458f9298" + "0000000000000000000000000000000000000000000000000000000000000001"))
একটি Geth কনসোলে, এটি নিম্নরূপ গণনা করা যেতে পারে:
> var key = "000000000000000000000000391694e7e0b0cce554cb130d723a9d27458f9298" + "0000000000000000000000000000000000000000000000000000000000000001"
undefined
> web3.sha3(key, {"encoding": "hex"})
"0x6661e9d6d8b923d5bbaab1b96e1dd51ff6ea2a93520fdc9eb75d059238b8c5e9"
তাই path হলো keccak256(<6661e9d6d8b923d5bbaab1b96e1dd51ff6ea2a93520fdc9eb75d059238b8c5e9>)। এটি এখন আগের মতো স্টোরেজ ট্রাই থেকে ডেটা পুনরুদ্ধার করতে ব্যবহার করা যেতে পারে:
curl -X POST --data '{"jsonrpc":"2.0", "method": "eth_getStorageAt", "params": ["0x295a70b2de5e3953354a6a8344e616ed314d7251", "0x6661e9d6d8b923d5bbaab1b96e1dd51ff6ea2a93520fdc9eb75d059238b8c5e9", "latest"], "id": 1}' localhost:8545
{"jsonrpc":"2.0","id":1,"result":"0x000000000000000000000000000000000000000000000000000000000000162e"}
দ্রষ্টব্য: একটি ইথেরিয়াম একাউন্টের জন্য storageRoot ডিফল্টভাবে খালি থাকে যদি এটি কোনো কন্ট্রাক্ট একাউন্ট না হয়।
লেনদেন ট্রাই
প্রতিটি ব্লকের জন্য একটি আলাদা লেনদেন ট্রাই রয়েছে, যা আবার (key, value) পেয়ার সংরক্ষণ করে। এখানে একটি পথ হলো: rlp(transactionIndex) যা এমন একটি কী-কে উপস্থাপন করে যা নিচের দ্বারা নির্ধারিত একটি ভ্যালুর সাথে মিলে যায়:
if legacyTx:
value = rlp(tx)
else:
value = TxType | encode(tx)
এই সম্পর্কে আরও তথ্য EIP 2718 (opens in a new tab) ডকুমেন্টেশনে পাওয়া যাবে।
রিসিপ্টস ট্রাই
প্রতিটি ব্লকের নিজস্ব রিসিপ্টস ট্রাই রয়েছে। এখানে একটি path হলো: rlp(transactionIndex)। transactionIndex হলো যে ব্লকে এটি অন্তর্ভুক্ত করা হয়েছিল তার মধ্যে এর ইনডেক্স। রিসিপ্টস ট্রাই কখনও আপডেট করা হয় না। লেনদেন ট্রাই-এর মতো, বর্তমান এবং লিগ্যাসি রিসিপ্ট রয়েছে। রিসিপ্টস ট্রাই-তে একটি নির্দিষ্ট রিসিপ্ট কোয়েরি করতে, এর ব্লকে লেনদেনের ইনডেক্স, রিসিপ্ট পেলোড এবং লেনদেনের ধরন প্রয়োজন। রিটার্ন করা রিসিপ্টটি Receipt ধরনের হতে পারে যা TransactionType এবং ReceiptPayload-এর কনক্যাটেনেশন হিসেবে সংজ্ঞায়িত করা হয় অথবা এটি LegacyReceipt ধরনের হতে পারে যা rlp([status, cumulativeGasUsed, logsBloom, logs]) হিসেবে সংজ্ঞায়িত করা হয়।
এই সম্পর্কে আরও তথ্য EIP 2718 (opens in a new tab) ডকুমেন্টেশনে পাওয়া যাবে।