കഴിഞ്ഞ ആഴ്ച ഞാൻ .night ഡൊമെയ്‌നുകൾ യഥാർത്ഥത്തിൽ എങ്ങനെയാണ് പ്രവർത്തിക്കുന്നത് എന്ന് മനസ്സിലാക്കാൻ ശ്രമിക്കുകയായിരുന്നു. ഒരു സ്പെക് ഷീറ്റ് (spec sheet) നോക്കിയല്ല, മറിച്ച് ബ്ലോക്ക്‌ചെയിനുമായി നേരിട്ട് ബന്ധപ്പെടുന്ന എന്തെങ്കിലും നിർമ്മിച്ചുകൊണ്ടാണ് ഞാൻ ഇത് ചെയ്തത്. അതിന്റെ ഫലമായി ഒരു ചെറിയ പ്രൊഫൈൽ വ്യൂവർ (profile viewer) തയ്യാറായി. tomin.night എന്നതുപോലെയുള്ള ഒരു പേര് ടൈപ്പ് ചെയ്താൽ, അത് ബ്ലോക്ക്‌ചെയിനിൽ നിന്ന് നേരിട്ട് പ്രൊഫൈൽ കണ്ടെത്തി നൽകുന്നു. രജിസ്ട്രാർ API-യോ ഓതന്റിക്കേഷൻ തടസ്സങ്ങളോ (auth wall) ഇതിലില്ല. ഒരു സ്മാർട്ട് കോൺട്രാക്റ്റും കുറച്ച് JavaScript-ഉം മാത്രമാണ് ഇതിന് പിന്നിലുള്ളത്.

ഇത് എങ്ങനെ പ്രവർത്തിക്കുന്നുവെന്നും, ഞാൻ എന്തൊക്കെ പഠിച്ചു എന്നും, എന്റെ ഒരു വൈകുന്നേരം മുഴുവൻ നഷ്ടപ്പെടുത്തിയ രണ്ട് പ്രത്യേക തടസ്സങ്ങൾ എന്തൊക്കെയാണെന്നും താഴെ വിവരിക്കുന്നു.

എന്തുകൊണ്ടാണ് ഓൺ-ചെയിൻ (On-Chain) പേരുകൾ പ്രധാനമാകുന്നത്

Midnight-ൽ, .night ഡൊമെയ്‌നുകൾ Midnames ആണ് നിയന്ത്രിക്കുന്നത്. ഒരു കമ്പനിയുടെ സെൻട്രൽ സെർവറിൽ നിന്ന് വിവരങ്ങൾ ചോദിക്കുന്നതിന് പകരം, ചെയിനിൽ നിലനിൽക്കുന്ന ഒരു സ്മാർട്ട് കോൺട്രാക്റ്റിൽ നിന്ന് നിങ്ങൾക്ക് നേരിട്ട് വിവരങ്ങൾ വായിക്കാം. ഡൊമെയ്ൻ, ഉടമസ്ഥൻ, ഉടമസ്ഥൻ ചേർത്തിട്ടുള്ള പ്രൊഫൈൽ ഫീൽഡുകൾ എന്നിവ രജിസ്ട്രി തന്നെ സൂക്ഷിക്കുന്നു. ഈ ഡാറ്റ ഓൺ-ചെയിൻ ആയതുകൊണ്ട് തന്നെ, ഐഡന്റിറ്റി (identity) എവിടെയും ഉപയോഗിക്കാൻ സാധിക്കും (portable). നിബന്ധനകൾ മാറ്റുകയോ സേവനം നിർത്തലാക്കുകയോ ചെയ്യാൻ കഴിയുന്ന ഒരു പ്ലാറ്റ്‌ഫോമിൽ നിന്നല്ല നിങ്ങൾ ഇത് വാടകയ്‌ക്കെടുക്കുന്നത്. നിങ്ങളുടെ കയ്യിൽ കീ (keys) ഉണ്ടെങ്കിൽ, ആ പേര് നിങ്ങളുടെ നിയന്ത്രണത്തിലായിരിക്കും.

ഈ മാറ്റം ഡെവലപ്പർമാർക്കും വളരെ പ്രധാനമാണ്. പരമ്പരാഗതമായ ഒരു DNS സിസ്റ്റത്തിലാണ് നിങ്ങൾ പ്രവർത്തിക്കുന്നതെങ്കിൽ, റേറ്റ് ലിമിറ്റുകൾ (rate limits), API കീകൾ, അപ്‌ടൈം വാഗ്ദാനങ്ങൾ എന്നിവയെല്ലാം നിങ്ങൾ പരിഗണിക്കേണ്ടി വരും. എന്നാൽ ഇവിടെ, കോൺട്രാക്റ്റ് സ്റ്റേറ്റ് (contract state) ആണ് സത്യത്തിന്റെ ഉറവിടം (source of truth). മറ്റെല്ലാ ആപ്ലിക്കേഷനുകളും വിവരങ്ങൾ വായിക്കുന്ന അതേ രീതിയിൽ തന്നെ നിങ്ങളുടെ ആപ്ലിക്കേഷനും അത് വായിക്കുന്നു. ഇവിടെ പ്രത്യേകമായ ഒരു പ്രിവിലേജ്ഡ് API ടയർ (privileged API tier) ഇല്ല.

കോഡ് വളരെ ലളിതമാണ്

@midnames/sdk ആണ് പ്രധാനപ്പെട്ട കാര്യങ്ങൾ ചെയ്യുന്നത്. ഒരു ഡൊമെയ്‌നെ ഒരു അഡ്രസ്സിലേക്കും പ്രൊഫൈലിലേക്കും മാറ്റാൻ (resolving) വെറും രണ്ട് വരികൾ മതി:

const provider = createDefaultProvider({ networkId: "mainnet" });
const result = await resolveDomain(provider, "tomin.night");

അത്രമാത്രം! പ്രൊവൈഡർ (provider) നെറ്റ്‌വർക്കിനെ ലക്ഷ്യം വെക്കുന്നു, റെസൊൾവർ (resolver) കോൺട്രാക്റ്റുമായി സംസാരിക്കുന്നു. ഒരു സക്സസ് ഫ്ലാഗ് (success flag) ഉൾപ്പെടുന്ന ഒരു റിസൾട്ട് ഒബ്‌ജക്റ്റ് ആണ് SDK നൽകുന്നത്. ഡൊമെയ്ൻ നിലവിലില്ലെങ്കിൽ, RPC പരാജയങ്ങൾക്കായി വലിയ രീതിയിലുള്ള എറർ ഹാൻഡ്‌ലിംഗോ (error handling) try-catch ബ്ലോക്കുകളോ ഉപയോഗിക്കേണ്ടതില്ല. അവിടെ ഒന്നുമില്ലെന്ന് SDK വ്യക്തമായി പറയും. ഇത് UI നിർമ്മാണം വളരെ എളുപ്പമാക്കുന്നു. പരാജയം കാരണം ഒരു പേര് കാണാത്തതാണോ അതോ നോഡ് (node) പ്രവർത്തിക്കാത്തതാണോ എന്ന് ഊഹിക്കാതെ തന്നെ, സക്സസ് ഫ്ലാഗ് ഉപയോഗിച്ച് നിങ്ങൾക്ക് “not found” സ്റ്റേറ്റ് കാണിക്കാൻ സാധിക്കും.

നിങ്ങൾക്ക് ലഭിക്കുന്നത് എന്താണ്

ലുക്കപ്പ് (lookup) വിജയിക്കുമ്പോൾ, ലഭിക്കുന്ന പേലോഡിൽ (payload) പ്രധാനപ്പെട്ട രണ്ട് കാര്യങ്ങളുണ്ട്.

Target എന്നത് ഡൊമെയ്ൻ ഏത് വാലറ്റ് അഡ്രസ്സിലേക്കാണോ വിരൽ ചൂണ്ടുന്നത് ആ അഡ്രസ് ആണ്. ഇതാണ് ഇതിന്റെ പ്രധാന ഉപയോഗം. ഇത് നീളമുള്ള ഒരു ഹെക്സ് (hex) അഡ്രസ്സിനെ മനുഷ്യർക്ക് വായിക്കാനും ടൈപ്പ് ചെയ്യാനും ഓർമ്മിച്ചുവെക്കാനും കഴിയുന്ന ഒന്നാക്കി മാറ്റുന്നു.

Fields പ്രൊഫൈൽ വിവരങ്ങൾ ഉൾക്കൊള്ളുന്നു. ഉടമസ്ഥൻ ഡൊമെയ്‌നുമായി ബന്ധിപ്പിച്ചിട്ടുള്ള സോഷ്യൽ ലിങ്കുകൾ, അവതാറുകൾ (avatars), ടെക്സ്റ്റ് റെക്കോർഡുകൾ എന്നിവയെല്ലാം ഈ ഘടനയ്ക്കുള്ളിലാണ്. ഈ ഫീൽഡുകൾ ഏതെങ്കിലും കമ്പനിയുടെ MongoDB ക്ലസ്റ്ററിലല്ല സൂക്ഷിച്ചിരിക്കുന്നത്. അവ കോൺട്രാക്റ്റ് സ്റ്റേറ്റിലെ ഫീൽഡുകളാണ്, അതായത് രജിസ്ട്രി എങ്ങനെ വായിക്കണമെന്ന് അറിയാവുന്ന ഏത് ആപ്പിനും ഒരേ പ്രൊഫൈൽ തന്നെ കാണിക്കാൻ കഴിയും. ഇതിനായി ഡാറ്റാബേസ് സിങ്ക് (database sync) ആവശ്യമില്ല.

എന്റെ വേഗത കുറച്ച രണ്ട് തടസ്സങ്ങൾ

ഈ നിർമ്മാണ പ്രക്രിയയിൽ എല്ലാം രണ്ട് വരികളും ഒരു സക്സസ് ഫ്ലാഗും മാത്രമായിരുന്നില്ല. നിങ്ങൾ ഇത് ആവർത്തിക്കാതിരിക്കാൻ, ഞാൻ നേരിട്ട രണ്ട് പ്രധാന തടസ്സങ്ങൾ ഇവിടെ വ്യക്തമാക്കുന്നു.

നെറ്റ്‌വർക്ക് വ്യത്യാസം (Network Mismatch)

SDK-യിൽ mainnet, preprod എന്നീ രണ്ട് എൻവയോൺമെന്റുകളും ലഭ്യമാണ്. "നിലവിലില്ലാത്ത" ഒരു ഡൊമെയ്‌നിനെക്കുറിച്ച് ഡീബഗ് (debug) ചെയ്യാൻ ഞാൻ ഒരുപാട് സമയം ചിലവഴിച്ചു. പേര് ശരിയായിരുന്നു, കോഡും ശരിയായിരുന്നു, പ്രൊവൈഡറും പ്രവർത്തിക്കുന്നുണ്ടായിരുന്നു. പ്രശ്നം എന്തെന്നാൽ, ഡൊമെയ്ൻ mainnet-ൽ രജിസ്റ്റർ ചെയ്തതായിരുന്നു, എന്നാൽ എന്റെ സ്ക്രിപ്റ്റ് തിരയുന്നത് preprod-ൽ ആയിരുന്നു. ഒരു ഡൊമെയ്ൻ കാണാത്തതാണെന്ന രീതിയിലായിരുന്നു എറർ വന്നത്, എന്നാൽ യഥാർത്ഥത്തിൽ അത് നെറ്റ്‌വർക്ക് കോൺടെക്സ്റ്റ് (network context) തെറ്റായതുകൊണ്ടായിരുന്നു.

നിങ്ങൾ ഒരു പേര് റിസോൾവ് ചെയ്യുമ്പോൾ പരാജയം സംഭവിക്കുകയാണെങ്കിൽ, മറ്റെന്തും പരിശോധിക്കുന്നതിന് മുമ്പ് പ്രൊവൈഡർ സെറ്റിംഗ്‌സ് പരിശോധിക്കുക. നിങ്ങളുടെ networkId, ഡൊമെയ്ൻ യഥാർത്ഥത്തിൽ മിന്റ് (mint) ചെയ്തിട്ടുള്ള നെറ്റ്‌വർക്കുമായി പൊരുത്തപ്പെടുന്നുണ്ടെന്ന് ഉറപ്പാക്കുക. കോൺട്രാക്റ്റ് ലോജിക് ആണ് പ്രശ്നം എന്ന് കരുതിയിരിക്കുമ്പോൾ കണ്ടെത്താൻ പ്രയാസമുള്ള, എന്നാൽ കാര്യങ്ങൾ മനസ്സിലാക്കിയ ശേഷം നോക്കുമ്പോൾ വളരെ ലളിതമായി തോന്നുന്ന ഒരു തെറ്റാണിത്.

സീരിയലൈസേഷൻ ബുദ്ധിമുട്ടുകൾ (Serialization Pain)

SDK-യിൽ നിന്ന് ലഭിക്കുന്ന ഡാറ്റ സാധാരണ JavaScript അല്ല. അതിൽ BigInt വാല്യൂസും Map ഒബ്‌ജക്റ്റുകളും അടങ്ങിയിരിക്കുന്നു. ഇത് നേരിട്ട് ഒരു ബ്രൗസറിലേക്ക് അയക്കാൻ JSON.stringify-ലേക്ക് നൽകാൻ ശ്രമിച്ചാൽ, അത് എറർ കാണിക്കുകയോ അല്ലെങ്കിൽ ഡാറ്റ നഷ്ടപ്പെടുകയോ ചെയ്യും. BigInt-ന് നേരിട്ട് JSON രൂപമില്ല, കൂടാതെ Map സാധാരണ ഒബ്‌ജക്റ്റുകളെപ്പോലെ സീരിയലൈസ് ചെയ്യപ്പെടുകയുമില്ല.

ഒടുവിൽ എനിക്ക് ഒരു കസ്റ്റം സീരിയലൈസർ (custom serializer) എഴുതേണ്ടി വന്നു. ഇത് റിസൾട്ട് ഒബ്‌ജക്റ്റിലൂടെ കടന്നുപോയി, BigInt വാല്യൂസിനെ സ്ട്രിംഗുകളാക്കി മാറ്റുകയും, റെസ്‌പോൺസ് സെർവറിൽ നിന്ന് പുറത്തുപോകുന്നതിന് മുമ്പ് Map ഇൻസ്റ്റൻസുകളെ സാധാരണ ഒബ്‌ജക്റ്റുകളാക്കി മാറ്റുകയും ചെയ്യുന്നു. നിങ്ങൾ ഒരു ഫ്രണ്ട്‌എൻഡിലേക്ക് (frontend) Midnight ഡാറ്റ നൽകുന്ന ഒരു API നിർമ്മിക്കുകയാണെങ്കിൽ, ഈ ഘട്ടം നേരത്തെ തന്നെ പ്ലാൻ ചെയ്യുക. അത് JavaScript ആയതുകൊണ്ട് മാത്രം SDK ഔട്ട്‌പുട്ട് നേരിട്ട് ഫ്രണ്ട്‌എൻഡിന് ഉപയോഗിക്കാൻ പാകത്തിലുള്ളതാണെന്ന് കരുതരുത്.

ആർക്കിടെക്ചർ (The Architecture)

ഞാൻ ബോധപൂർവ്വം വളരെ ലളിതമായ ഒരു സ്റ്റാക്ക് ആണ് ഉപയോഗിച്ചത്. ബാക്കെൻഡ് എന്നത് Express പ്രവർത്തിപ്പിക്കുന്ന ഒരു Node സെർവറാണ്. ഇത് @midnames/sdk ഇംപോർട്ട് ചെയ്യുന്നു, റെസല്യൂഷൻ ലോജിക് പ്രവർത്തിപ്പിക്കുന്നു, സീരിയലൈസേഷനിലെ സങ്കീർണ്ണതകൾ കൈകാര്യം ചെയ്യുന്നു, കൂടാതെ ക്ലീൻ ആയ JSON നൽകുന്നു. ഫ്രണ്ട്‌എൻഡ് വെറും HTML-ഉം വാനില (vanilla) JavaScript-ഉം ആണ്. ബിൽഡ് സ്റ്റെപ്പുകളില്ല, ഫ്രെയിംവർക്കുകളില്ല, വാലറ്റ് അഡാപ്റ്ററുകളുമില്ല.

ചില പ്രായോഗിക കാരണങ്ങളാൽ ബ്രൗസറിന് പകരം ബാക്കെൻഡിൽ SDK പ്രവർത്തിപ്പിക്കാനാണ് ഞാൻ തീരുമാനിച്ചത്. ഇത് പ്രൊവൈഡർ കോൺഫിഗറേഷനുകളെ ക്ലയന്റിൽ നിന്ന് അകറ്റി നിർത്തുന്നു, സീരിയലൈസേഷനിലെ പ്രശ്നങ്ങൾ പരിഹരിക്കാൻ ഒരൊറ്റ സ്ഥലം നൽകുന്നു, കൂടാതെ ഫ്രണ്ട്‌എൻഡിന് ഡാറ്റ ഫെച്ച് ചെയ്ത് റെൻഡർ ചെയ്യുക എന്നത് മാത്രമായി മാറുന്നു.

എന്നെ ഏറ്റവും അത്ഭുതപ്പെടുത്തിയ കാര്യം ഇതാണ്: ഒരു പേര് റെസൾവ് ചെയ്യുന്നത് ഒരു പബ്ലിക് റീഡ് ഓപ്പറേഷൻ (public read operation) ആണ്. ഇതിന് വാലറ്റ് കണക്ഷൻ ആവശ്യമില്ല. ഒരു സിഗ്നേച്ചറും ആവശ്യമില്ല. ഉപയോക്താവ് എന്തിനോടും ലോഗിൻ ചെയ്യേണ്ട കാര്യവുമില്ല. ഡൊമൈൻ നിലവിലുണ്ടെങ്കിൽ, ആർക്കും കോൺട്രാക്ട് സ്റ്റേറ്റ് (contract state) കാണാൻ സാധിക്കും. ഓരോ ഇന്ററാക്ഷനും "connect wallet" എന്നതിൽ തുടങ്ങുന്ന സാധാരണ web3 രീതിയിൽ നിന്നുള്ള വലിയൊരു മാറ്റമാണിത്. ഒരു പബ്ലിക് വെബ്‌സൈറ്റ് കാണുന്നതുപോലെ തന്നെ, Midnight-ൽ ഐഡന്റിറ്റി വായിക്കുന്നത് പെർമിഷൻലെസ്സ് (permissionless) ആണ്.

യഥാർത്ഥ പാഠം

ഈ വ്യൂവർ നിർമ്മിച്ചത് ബ്ലോക്ക്‌ചെയിൻ ഡെവലപ്മെന്റിലെ ഏറ്റവും പ്രയാസകരമായ കാര്യം ബ്ലോക്ക്‌ചെയിൻ തന്നെ അല്ല എന്ന് എന്നെ ഓർമ്മിപ്പിച്ചു. ഒരു സെൻട്രൽ ഡാറ്റാബേസ് ഇല്ലാതെ തന്നെ ആളുകൾക്ക് അവരുടെ പേരും പ്രൊഫൈലും സ്വന്തമാക്കാൻ അനുവദിക്കുക എന്ന പ്രയാസകരമായ പ്രശ്നം Midnight ഇതിനകം പരിഹരിച്ചിട്ടുണ്ട്. ഒരു ബിൽഡറുടെ കാഴ്ചപ്പാടിൽ, ഏത് നെറ്റ്‌വർക്കാണ് ഉപയോഗിക്കുന്നത് എന്ന് ഓർത്തെടുക്കുന്നതും ഡാറ്റാ ടൈപ്പുകൾ ക്ലീൻ ചെയ്യാൻ ഒരു ഹെൽപ്പർ ഫംഗ്ഷൻ എഴുതുന്നതുമായിരുന്നു പ്രയാസകരമായ കാര്യം.

ഈ പ്രോട്ടോക്കോൾ നിങ്ങൾക്ക് പോർട്ടബിൾ ഐഡന്റിറ്റി (portable identity) നൽകുന്നു. ഒരു ഡെവലപ്പർ എന്ന നിലയിൽ നിങ്ങളുടെ ജോലി അത് ശരിയായി വായിക്കുകയും ഉപയോക്താവിന് തടസ്സമില്ലാതെ കാര്യങ്ങൾ ചെയ്യാൻ അനുവദിക്കുകയും ചെയ്യുക എന്നത് മാത്രമാണ്. ആർക്കിടെക്ചർ ലളിതമായി സൂക്ഷിക്കുക, ചെയിൻ-ഫേസിംഗ് ലോജിക്കിനെ (chain-facing logic) UI-ൽ നിന്ന് വേർതിരിക്കുക, പബ്ലിക് റീഡുകളെ അവ എങ്ങനെയുള്ളതാണോ അങ്ങനെ തന്നെ കാണുക: ഒരു ഡിസ്ട്രിബ്യൂട്ടഡ് ലെഡ്ജറിൽ (distributed ledger) പ്രവർത്തിക്കുന്ന സാധാരണ ഡാറ്റാബേസ് ക്വറികൾ എന്ന് മാത്രം കരുതുക.

നിങ്ങൾക്ക് കോഡ് കാണണമെന്നോ സ്വയം പ്രവർത്തിപ്പിച്ചു നോക്കണമെന്നോ ഉണ്ടെങ്കിൽ, ഇതിന്റെ പൂർണ്ണമായ സോഴ്സ് https://github.com/tomiin/midnames-profile-viewer എന്ന ലിങ്കിൽ ലഭ്യമാണ്.