Solidity
ساناز موسوی | 2018.07.18

آموزش گام به گام زبان برنامه نویسی Solidity برای ساخت DApp اتریوم

بنیاد اتریوم از همان روز های اولیه ‌ی پروژه، یعنی در حدود سال‌ های ۲۰۱۳ و اوایل ۲۰۱۴، دنیای بلاک چین را به لرزه درآورد. اتریوم «نسخه دوم بیت‌ کوین» را استارت زد؛ چیزی که ما از آن به عنوان جنبش  بلاک چین یاد می‌ کنیم. درست زمانی که پس از اولین «حباب»، قیمت بیت‌ کوین تا ۱۰۰۰ دلار افزایش یافت، توجه همه را به خود جلب کرد. اتریوم، پروژه ‌ی بلاک چین ارز رمز نگاری ‌شده، ether، شبیه به بیت‌ کوین است، با این تفاوت که اتریوم، ماشین مجازی تورینگ کامل و قابلیت پردازش تعبیه ‌شده در . اجرای نود ها را اضافه کرده است.در این مقاله آموزش برنامه نویسی به زبان Solidity  به شما آموزش داده شده است

آموزش گام به گام زبان برنامه نویسی Solidity برای ساخت DApp اتریوم

ماشین مجازی اتریوم (EVM) به نود های اتریوم اجازه می ‌دهد تا اطلاعات را در قبال دستمزد، ذخیره و پردازش کنند. این تراکنش‌ ها در راستای پاسخ به رویداد های دنیای واقعی است؛ همچنین فرصت‌ های جدید بسیاری برای حمایت از برنامه ‌های زنجیره ‌ای، که تا قبل از این هیچوقت برای توسعه ‌دهندگان و کاربران دنیای واقعی در دسترس نبودند، به ارمغان می ‌آورد.

Mihai Alisie از‌ بنیان‌ گذاران اتریوم، در مورد قرارداد هوشمند اتریوم اینگونه توضیح می‌ دهد:

«قرارداد های هوشمند راهی برای مردمِ سراسر دنیا است تا کار های خود را حتی زمانی که یک زبان یا یک ارز مشترک نداشته باشند انجام دهند.»

بنابراین با این چشم ‌انداز و این ایده که می‌ توانیم قوانین یک قرارداد تجاری را به وسیله یک زبان ماشین ساده تعریف کنیم تا مردم را در کنار یکدیگر جمع کرده و به آن‌ ها این اجازه را بدهیم تا کسب و کار ها را به گونه ‌ای قابل اطمینان، امن، به ‌روز و خودکار هدایت کنند، شروع می‌ کنیم.

زبان برنامه نویسی solidity یک ابزار است که از آن برای تولید کد سطح ماشین استفاده می‌ کنیم تا بتوانیم بر روی EVM اجرا نماییم. solidity یک زبان برنامه ‌نویسی با یک کامپایلر است که کد سطح بالا و خوانا برای انسان را برداشته و آن را به دستور های ساده مانند «put data into a register»، «add data from two registers»، «jump back to instruction at memory point xxxxx» تبدیل می‌ کند که اساس برنامه اجرایی هر ریز پردازنده را تشکیل می‌ دهند.

بیشتر بخوانید: ترجمه وایت پیپر ارز رمزنگاری شده اتریوم (Ethereum)

بنابراین زبان برنامه ‌نویسی solidity تنها یکی از چندین زبانی است که می‌ توانند به بایت ‌کد (bytecode) EVM  کامپایل شوند؛ زبان برنامه نویسی دیگری که کار مشابهی انجام می ‌دهد Serpent نام دارد. هر زبان برنامه نویسی ممکن است تعدادی ابزار کامپایلر داشته باشد اما همه ‌ی آن ‌ها یک کار، یعنی همان تولید بایت کد EVM در سطح ماشین برای اجرا شدن در نود های اتریوم، که سرانجام به پرداخت می‌ انجامد را انجام می ‌دهند.

چگونه زبان برنامه نویسی Solidity را بیاموزیم؟

زبان برنامه نویسی Solidity به خودی خود زبان ساده ‌ای است. در حقیقت، این زبان سینتکسی بسیار مشابه به ECMAScript یا همان جاوا اسکریپت دارد. نکات کلیدی‌ ای برای مرور سند Ethereum Design Rationale وجود دارد؛ از جمله اینکه با یک مدل stack-and-memory با حافظه ‌ی ۳۲ بایت کار می‌ کنیم. EVM اجازه دسترسی به برنامه «stack» را که شبیه به یک فضای رجیستر است و در آن می ‌توانیم آدرس‌ های حافظه را به یکدیگر بچسبانیم تا برنامه را وادار به تکرار حلقه و یا پرش (برای کنترل مستمر برنامه) بکنیم، را می ‌دهد. همچنین، دارای یک «memory» موقت و قابل گسترش، یک حافظه «storage» دائمی‌ که در واقع درون بلاک چین دائمی نوشته شده است. از همه مهم ‌تر، EVM نیاز به قطعیت کامل در قرارداد های هوشمند دارد.

آموزش گام به گام زبان برنامه نویسی Solidity برای ساخت DApp اتریوم

این نیاز به قطعیت، همان دلیلی است که شما تابع «random()» را در زبان برنامه ‌نویسی Solidity مشاهده نمی ‌کنید. وقتی که یک بلاک اتریوم استخراج می‌ شود، قرارداد هوشمند اجرا شده و توابع درون بلاک فراخوانی می‌ شوند (به این معنی که به آخرین بلوک متصل می‌ شود) و در نودی که بلوک را استخراج می‌ کند اجرا می‌ شود. سپس بلوک جدید در میان دیگر نود ها نیز منتشر می ‌شود و هر نود به صورت مستقل بلوک را تایید می ‌کند. این تایید شامل انجام همان تغییرات در کپی هر نود از بلاک چین است. اینجا همان جایی است که اگر قرارداد هوشمند به طور غیر قطعی عمل کند با شکست مواجه می ‌شود. اگر بقیه‌ ی نود ها نتوانند به توافقی درباره‌ ی وضعیت بلاک چین بعد از بلوک جدید و اجرای قرارداد آن برسند، در این صورت شبکه به معنای واقعی از کار می ‌افتد.

به همین دلیل، قرارداد های هوشمند اتریوم (و به طور کلی قرارداد های هوشمند در هر سیستم بلاک چینی) باید قطعیت داشته باشند. در این صورت، نود های شبکه همیشه می‌ تواند بلوک ‌های جدیدی که برای تایید می ‌آیند را ارزیابی کنند و برای حفظ آن‌ ها توافق نمایند تا از این طریق بلاک چین تداوم یابد.

محدودیت دیگری که در قرارداد های هوشمند EVM با آن مواجه می‌ شوید، ناتوانی در دسترسی به داده ‌های خارج از «memory»، «storage» (ما نمی ‌خواهیم که قرارداد هوشمند این قابلیت را داشته باشد که بتواند hard-drive نودی که روی آن اجرا می‌ شود را بخواند یا پاک کند)، و درمورد ناتوانی در پرس ‌و جو از منابع خارج، مشابه JQuery است. همچنین، توانایی دسترسی به بسیاری از توابع کتابخانه ‌ای مانند دستور های JSON را نداریم؛ در حقیقت برای انجام این فرایند ها یا ذخیره‌ ی مقدار زیادی داده در خود بلاک چین اتریوم، هزینه‌ ی زیادی لازم است.

وقتی شما یک قرارداد هوشمند که شامل تغییر وضعیت یا محاسبات (هر عملی غیر از صرفا خواندن از storage) می‌ شود را فراخوانی می ‌کنید، شما یک «gas cost» برای کار هایی که توسط قرارداد هوشمند انجام می ‌شود متحمل می ‌شوید. این gas cost مرتبط با مقدار کار محاسباتی لازم برای اجرای تابع شماست. این نوعی از سیستم «micropayment for microcomputing» است، جایی که می‌ توانید انتظار داشته باشید که همیشه مقدار gas ثابتی برای انجام محاسبات معین، بپردازید.

آموزش گام به گام زبان برنامه نویسی Solidity برای ساخت DApp اتریوم

قیمت gas به طور کلی ثابت است، به این معنی که وقتی اتریوم در بازار جهانی با افزایش قیمت مواجه می شود قیمت gas در مقابل اتریوم کاهش می‌ یابد. بنابراین، وقتی شما توابع یک قرارداد هوشمند را فراخوانی می‌ کنید، می‌ توانید تخمینی از مقدار gas که باید قبل از آن بپردازید را به دست آورید. همچنین شما باید قیمتی که تمایل به پرداخت آن دارید را نیز مشخص کنید (که واحد آن ether بر gas است). سپس، نود های استخراج‌ کننده می ‌توانند تصمیم بگیرند که آیا این نرخ برای قرار دادن تابع فراخوانی شده قرارداد هوشمند شما، در بلوک بعدی خودشان کافی هست یا نه.

بیشتر بخوانید: چطور می توانیم یک توسعه دهنده اتریوم (Ethereum) شویم؟

قرارداد های هوشمند، آدرس متعلق به خودشان را دارند که می‌ توانند با آن Ether دریافت و ارسال کنند. مسیر فراخوانی توابع در قرارداد های هوشمند قابل ردیابی است، بنابراین می‌ توان فهمید که تابع توسط «مالک» یا «admin» فراخوانی شده و نیز، بر اساس توابع پیش‌ فرض عمل می‌ کنند. آن ‌ها این قابلیت را دارند که داده ‌ها را از روی بلاک چین اتریوم بخوانند و به اطلاعات بلوک‌ های قدیمی‌ تر دسترسی داشته باشند. اما آیا قرارداد ‌های هوشمند در دنیای کوچک و با قطعیت خودشان «حبس» شده‌ اند تا فقط از داده ‌های ذخیره شده در خود بلاک چین اتریوم آگاهی داشته باشند؟

به هیچ وجه! در اینجا مفهوم «oracles» وارد می ‌شود. ما می‌ توانیم یک oracle را فراخوانی کنیم که چیز های قابل اطمینانی در مورد دنیای بیرون به ما خواهد گفت و اطلاعات را داخل قرارداد هوشمند اعمال خواهد کرد. نکته‌ ی کلیدی اینجاست که حتی خودِ رویداد های درون دنیای واقعی نیز قطعی نیستند. Oracle همیشه می‌ تواند پاسخ صادقانه ‌ای به درخواست نود برای پاسخ در مورد آنچه قطعا اتفاق افتاده بدهد. لذا نود ها همچنان می ‌توانند به یک توافق برسند.

آموزش گام به گام زبان برنامه نویسی Solidity برای ساخت DApp اتریوم

در واقع، آنچه اتفاق می‌ افتد این است که کسی بگوید چه دیتایی نیاز دارد و سرویس مورد اعتمادش یک ID شناسایی (که در اینجا هش اطلاعات است) و یک کلید برای امضای نتایج فراهم کند، سپس کسی یک تراکنش به قرارداد بفرستد و در نهایت در انتظار نتایج بمانیم. هنگامی که نتایج مشخص شدند، سرویس مورد اعتماد آن را با کلید خود امضا می ‌کند. کسی تراکنش دیگری که حاوی داده‌ های امضا شده به قرارداد است، ارسال می‌ کند و قرارداد، کاری را انجام می‌ دهد که قرار است اتفاق بیفتد.

این کار را می ‌توان به صورت زنجیر‌‌ وار یا غیر زنجیر وار انجام داد. به طور معمول، چنین فرایندی بخشی از DApp است. آنها فرایند را غیر زنجیر وار انجام می‌دهند؛ به این معنی که فقط داده ‌های امضا شده و یک کلید ارائه می‌ دهند. کاربران این اطلاعات را از سایت می‌ گیرند و به قرارداد هوشمند خودشان می‌ فرستند. Oraclize این فرایند را زنجیر ‌وار انجام می ‌دهد، بدین ترتیب که قرارداد شما یک تقاضا به قرارداد آن‌ ها ارسال می‌ کند، قرارداد آنها این درخواست را بخش رویداد ها ثبت کرده و یک شناسه برای شما می‌ فرستد. آنها فرایندی برای مشاهده رویداد ها دارند و وقتی دیتا آماده شد، آن را به صورت امضا شده برای شما می‌ فرستند تا بتوان فرستنده قرارداد را ارزیابی کرد (در واقع، راه دیگری برای ارزیابی شخص امضا کننده اطلاعات است)؛ و در نهایت، آنچه که باید به اجرا درآید اجرا می ‌شود.

بنابراین، oracle در تئوری قابل اطمینان عمل می‌ کند: یک oracle مقداری داده ‌دارد (منبعی از نوسان قیمت در جهان واقعی) و آن داده ‌ها را درون «storage» در یک قرارداد هوشمند Oracle ساده ثبت می‌ کند، چیزی شبیه به این:

function storeTickerData(bytes8 symbol, uint open, uint high, uint low, uint close)
    onlyOracle
    returns(bool success)
  {
    # store ticker data on-chain
    tickerData[symbol][block.number] = [open,high,low,close]
    # tickerData is a map of maps, string => uint => array[uint,uint,uint,uint]
    return true;
  }

شرکت ‌هایی وجود دارند که در قابل اطمینان بودن Oracle ها و طراحی سیستم‌ هایی برای متمایز ساختن خودشان برای قابل اطمینان بودن از طریق datafeed، تخصص دارند. اگر نگاهی به Oraclize documentation داشته باشیم با این متن جالب مواجه می‌ شویم:

«در حقیقت، هدف این نیست که توسعه‌ دهندگان قرارداد های هوشمند را مجبور به داشتن Oraclize قابل اعتماد داده ‌هایی که به آن نیاز دارند بکنیم. بدون هیچگونه حمایت و پشتیبانی هم oraclize می‌ تواند به راحتی داده ‌ها را دستکاری کند. به این دلیل، به منظور تکمیل این کار پیچیده، oraclize اطلاعات درخواست شده را با روش «اثبات صحت» باز می‌ گرداند. برای مثال، داده هایی توسط ارائه دهنده دیتا ارسال می ‌شوند که به وضوح توسط قرارداد هوشمند مورد نیاز است.»

بنابراین باید «اثبات صحت» را با جزئیات بیشتر بررسی کنید که در این مجال نمی‌گنجد، اما ایده این است که حتی وقتی خوره ‌های (nerds) جدید اتریوم در آینده نه چندان دور شروع به کار کنند، نود های اتریوم ‌شان به گردش درآید و بلاک چین آن ‌ها با شبکه همگام شود. همه‌ ی داده‌ هایی که نیاز دارند، همه ‌ی قرارداد های هوشمندِ «oracle-dependent» زنجیر وار، قابل اطمینان و به طور دائمی برای دانلود در دسترس آن ‌هاست. بحث درمورد کسانی که «gas» برای oracle پرداخت می ‌کنند تا همه‌ ی این داده نویسی‌ ها را درباره ‌ی وقایع دنیای حقیقی انجام دهد، موضوع دیگری است. اما اساسا، دریافت پیش‌ پرداخت از درخواست ‌کننده‌ ی داده راه ‌های متنوعی دارد. موضوع جالب اینجاست که با اینکه نمی‌ توانیم قرارداد هوشمند «پرتاب تاس» را بنویسیم، اما این امکان وجود دارد که بتوان قرارداد هوشمندی نوشت که نسبت به پرتاب تاس از خود واکنش نشان دهد، همانطور که توسط oracle انجام شد.

پایه ‌های solidity

پایه‌ های نوشتن یک کد ساده و راه‌ اندازی آن

اجازه دهید با یک قرارداد هوشمند ابتدایی اتریوم که به زبان solidity نوشته شده است شروع کنیم. می ‌توانیم از کد کوچک بالا ببینیم که سینتکس بسیار ساده ‌ای برای تعریف تابع و بعضی از انواع داده ‌های پایه مانند «unit» (اعداد صحیح بدون علامت)، «string» و همچنین انواع متغیر هایی مانند «address» که شامل روش ‌های «.balance» و «.transfer» می ‌شود، دارد. این روش‌ ها به طور تخمینی موجودی یک آدرس اتریوم را بر می ‌گردانند و به قرارداد هوشمند این اجازه را می‌ دهد که واقعا ether به آن آدرس بفرستد (با واحد های تعریف شده در Wei).

آرایه ‌ها،  enum و عملگر ها، hash data structures «mapping» نامیده می ‌شوند.  برخی واحد های استاندارد و متغیر های از پیش تعریف شده، شامل چیز هایی مانند ارتفاع بلوک (blockheight)، last block timestamp و برخی توابع مفید SHA-hash و توابع دستکاری address/key، می ‌باشد. یک تناقض مهم در مورد solidity هنگام کار با آرایه ‌های چند بعدی ایندکس‌ گذاری آرایه ‌ها، به ترتیب معکوس بسیاری از زبان‌ هاست، به صورتی که:

«یک آرایه با سایز k و عنصر نوع T به صورت T[k] و یک آرایه با اندازه پویا به صورت T[] نوشته می‌ شود. به عنوان یک نمونه، یک آرایه‌ ی متشکل از پنج آرایه ‌ی پویا از unit به صورت unit[][5] قابل استفاده است (توجه کنید که نشانه ‌گذاری در مقایسه با برخی زبان‌ های دیگر به صورت معکوس است). برای دسترسی به unit دوم در سومین آرایه‌ ی پویا، باید از x[2][1] استفاده کنید (اندیس‌ ها مبتنی بر صفر هستند و دسترسی به صورت متضاد از آنچه تعریف شده است کار می‌کند).»

خب، تا به اینجا به قدر کافی با ابزار های پایه‌ ای آشنا شدید.

در ادامه روی یک اپلیکیشن شبکه اجتماعی کار می کنیم؛ اپلیکیشنی که در آن مردم می‌ توانند تعدادی عکس و مقداری اطلاعات در مورد خودشان روی بلاک چین پست کنند. هر کاربر یک آدرس اتریوم و یک «handle» یا نام کاربری مرتبط با حسابش خواهد داشت. به یک سری اطلاعات جمعیت شناختی که مجوز ورود افراد را بررسی کند نیز نیاز خواهیم داشت. همچنین، یک صفحه‌ ی ساده ی جست‌ و جو خواهیم داشت؛ جایی که شما می ‌توانید مستقیما به دنبال کاربر مورد نظرتان بگردید و بررسی کنید که عکسی به پروفایل خود اضافه کرده است یا نه.

آموزش گام به گام زبان برنامه نویسی Solidity برای ساخت DApp اتریوم

ساخت اپلیکیشن غیرمتمرکز (DApp) بر پایه قرارداد هوشمند اتریوم

اولین گام راه اندازی بستر آزمایش (test-bed) truffle است که امکان کامپایل کردنِ آسانِ قرارداد هوشمند را فراهم می‌ کند. کامپایل و پیاده سازی قرارداد های هوشمند شامل ایجاد معاملات طولانی امضا شده می ‌شود، اما truffle به ما اجازه می ‌دهد تا کامپایل یا پیاده ‌سازی را انجام دهیم و قرارداد هوشمند را با دستوراتی ساده آزمایش کنیم. ابزار های دیگری نیز برای انجام چنین کاری وجود دارند. با این حال این آموزش را با truffle پیش می بریم. دموی این پروژه در گیت هاب برای آموزش به علاقه مندان ایجاد شده است. با دستورات ساده نصب (برنامه تحت محیط debian linux) آغاز می‌کنیم:

tectract@blockgeeks/ethereum-demo-tools> npm i -g truffle
  # if you run into permissions error globally installing node modules, see here

  tectract@blockgeeks/ethereum-demo-tools> mkdir Geekt  # I'm going to call my social application "Geekt"
  tectract@blockgeeks/ethereum-demo-tools> cd Geekt
  tectract@blockgeeks/ethereum-demo-tools/Geekt> truffle init # this creates a few folders and setup files, within the Geekt/ folder
  tectract@blockgeeks/ethereum-demo-tools/Geekt > ls
  contracts  migrations  test  truffle.js                  # we have created three folders, and one new file called truffle.js

  tectract@blockgeeks/ethereum-demo-tools/Geekt > ls contracts/
  ConvertLib.sol  MetaCoin.sol  Migrations.sol             # we have some basic template contracts generated by Truffle

  tectract@blockgeeks/ethereum-demo-tools/Geekt > ls migrations/
  1_initial_migration.js  2_deploy_contracts.js            # a couple fi

آموزش گام به گام زبان برنامه نویسی Solidity برای ساخت DApp اتریوم

یک مثال ساده از قرارداد هوشمند

حال، یک قرارداد با نام Geekt.sol، در پوشه‌ی /Geekt/contracts، که در هنگام فراخوانی «truffle init» ساخته شده، ایجاد می‌کنم. کد کامل قرارداد از اینجا قابل دسترس است، ما فقط به جالب ترین نکات اشاره خواهیم کرد. در خط بالای فایل، نسخه خاص کامپایلر، بعضی از تعریفات ابتدایی سینتکس‌ها و تعریف متغیر ها مشخص شده است.

pragma solidity ^0.4.4;
contract Geekt {

  address GeektAdmin;

  mapping ( bytes32 => notarizedImage) notarizedImages; // this allows to look up notarizedImages by their SHA256notaryHash
  bytes32[] imagesByNotaryHash; // this is like a whitepages of all images, by SHA256notaryHash

  mapping ( address => User ) Users;   // this allows to look up Users by their ethereum address
  address[] usersByAddress;  // this is like a

وقتی یک تابعِ تغییر حالت را در دنیای حقیقی فراخوانی می ‌کنید، باید مشخص نمایید که مایلید چه مقدار Ether برای انجام آن بپردازید. خوشبختانه می‌ توانید تخمین هزینه ‌ی gas cost و gas price را پیشتر درخواست کنید که معمولا خیلی هم دقیق است. چنانچه استخراج‌ کننده ‌های اتریوم تشخیص دهند که کارمزد کافی را پرداخت کرده‌ اید، تراکنش تغییر حالت شما در بلوک بعدی گنجانده می‌ شود. در حقیقت، باید منتظر بمانید تا توابع، وقتی بلوک جدید استخراج شد، بازگردند. خواندن داده خارج از ساختار های «storage» رایگان است و لازم نیست منتظر بلوک بعدی بمانید؛ نود ها فقط داده ‌ها را می ‌خوانند و آن ‌ها را به شما باز می ‌گردانند.

بنابراین، نگاشت کاربران (Users mapping)، storage اصلی شی‌‌ء ماست که اجازه می ‌دهد شی‌ء های User (کاربر) بسازیم و آن‌ها را با استفاده از آدرس ببینیم. نگاشت‌ ها برای ذخیره ‌ی داده و بازیابی سریع آن بسیار کاربردی هستند، اما هیچ راه آسانی برای تکرار نگاشت وجود ندارد. بنابراین همانطور که می‌ بینید یک آرایه ‌ی آدرس usersByAddress ساخته‌ ایم که هر آدرس شناخته شده‌ ای از کاربر را در سیستم ما ذخیره می ‌کند.

از نگاشت notarizedImages استفاده می کنیم تا یک شیء «image» درون storage بسازیم و آنها را با هش SHA256 مرتبط با داده‌ ی image جست و جو کنیم. اندیس‌ های notaryHash سی و دو بایت طول دارند.

همچنین یک آرایه ‌ی ۳۲ بایتی ImagesByNotaryHash داریم که یک لیست از همه ‌ی notrayHash ها است و مانند white-pages به ما اجازه می‌ دهد همه تصاویری را که مورد تایید قرار گرفته‌ اند، تکرار کنیم.

struct notarizedImage {
    string imageURL;
    uint timeStamp;
  }

  struct User {
    string handle;
    bytes32 city;
    bytes32 state;
    bytes32 country;
    bytes32[] myImages;
  }

این ‌ها ساختار های بسیار ابتدایی هستند و همچنین اشیاء «storage» را ارائه می ‌دهند، بنابراین واقعا Ether هزینه می‌ کنند تا «ساخته شوند» و داده‌ ی درون آنها درون بلاک چین اتریوم ذخیره می‌ شود. در حقیقت ما از نگاشت‌ های خارجی و آرایه‌ ها استفاده می ‌کنیم تا مسیر ذخیره users یا images را در حافظه‌ ی بلاک چین داشته باشیم. ساختار notarizedImage به سادگی URL را در تصویر ذخیره می ‌کند و هشدار timestamp ما را از زمان notarize تصویر آگاه می ‌نماید. ساختار User برخی از اطلاعات پایه ی کاربران مثل شهر، ایالت، کشور و همچنین یک آرایه از تصاویری که کاربر به حساب کاربری خود اضافه کرده را ذخیره می ‌کند. شبیه یک ورژن کوچک کاربر به کاربر از شیء white-pages جهانی imagesByNotaryHash.

function registerNewUser(string handle, bytes32 city, bytes32 state, bytes32 country) returns (bool success) {
    address thisNewAddress = msg.sender;
    // don't overwrite existing entries, and make sure handle isn't null
    if(bytes(Users[msg.sender].handle).length == 0 && bytes(handle).length != 0){
      Users[thisNewAddress].handle = handle;
      Users[thisNewAddress].city = city;
      Users[thisNewAddress].state = state;
      Users[thisNewAddress].country = country;
      usersByAddress.push(thisNewAddress);  // adds an entry for this user to the user 'whitepages'
      return true;
    } else {
      return false; // either handle was null, or a user with this handle already existed
    }
  }

در اینجا تابع registerNewUser را می ‌بینید. این تابع شهر، ایالت، کشور را به عنوان متغیر های ورودی در نظر می‌ گیرد و مقدار true یا false را برای موفقیت یا عدم موفقیت بر می‌ گرداند. اما عدم موفقیت به چه معنی است؟ در واقع ما نمی ‌خواهیم که کاربران بیش از اندازه اطلاعات وارد کنند، بنابراین چیزی شبیه به سیستم اولین اقدام (first-claim system) برای دسته‌ ها را به کار می‌ بریم. اگر کاربری با این اطلاعات در حال حاضر موجود باشد، مقدار null را بر می ‌گردانیم، بنابراین یک if شرطی گذاشته ‌ایم تا آن را بررسی کند. همچنین، اجازه ایجاد نام کاربری بدون دسته را به کاربران نمی ‌دهیم. thisNewAddress تابع را فراخوانی می‌ کند. مشاهده می ‌کنید که از تابع خاص msg.sender برای دریافت اطلاعات استفاده می‌ کنیم؛ این آدرسی است که تراکنش «do this function» وقتی کسی تابع قرارداد هوشمند را فراخوانی کند، فرستاده می‌ شود و باید Ether حقیقی برای انجام آن پرداخت شود. بنابراین یک شیء «mapping» به صورت پیش فرض null است. زمانی که این خط را فراخوانی می ‌کنیم:

Users[thisNewAddress].handle = handle;

این یک شیء جدید User در نگاشت User ساخته می‌ شود و اطلاعات (مشابه شهر، ایالت و کشور) را ثبت می‌ کند. همچنین، توجه داشته باشید که آدرس جدید کاربر را، قبل از اینکه true را برگردانیم، به userByAddress global Users “white-pages”  منتقل می ‌کنیم.

 function addImageToUser(string imageURL, bytes32 SHA256notaryHash) returns (bool success) {
    address thisNewAddress = msg.sender;
    if(bytes(Users[thisNewAddress].handle).length != 0){ // make sure this user has created an account first
      if(bytes(imageURL).length != 0){   // ) {  // couldn't get bytes32 null check to work, oh well!
        // prevent users from fighting over sha->image listings in the whitepages, but still allow them to add a personal ref to any sha
        if(bytes(notarizedImages[SHA256notaryHash].imageURL).length == 0) {
          imagesByNotaryHash.push(SHA256notaryHash); // adds entry for this image to our image whitepages
        }
        notarizedImages[SHA256notaryHash].imageURL = imageURL;
        notarizedImages[SHA256notaryHash].timeStamp = block.timestamp; // note that updating an image also updates the timestamp
        Users[thisNewAddress].myImages.push(SHA256notaryHash); // add the image hash to this users .myImages array
        return true;
      } else {
        return false; // either imageURL or SHA256notaryHash was null, couldn't store image
      }
      return true;
    } else {
      return false; // user didn't have an account yet, couldn't store image
    }
  }

تابع addImageToUser کاملا شبیه به تابع registerNewUser است. این تابع شما را توسط آدرس ارسالی از طریق شیء خاصِ msg.sender جست ‌و جو می ‌کند و اینکه کاربر در دسته ‌ای ثبت شده است را بررسی می‌ کند؛ چرا که آدرس قبل از اینکه سعی کنید عکسی را به حساب کاربری خود اضافه کنید وجود داشته است. به کاربران اجازه می ‌دهیم تا notaryHash خود را به پروتکل white-pages جهانی عکس‌ها، در صورتی که از قبل وجود نداشته باشد، اضافه کنند. این کار برای جلوگیری از کشمکش کاربران برای ورود به پروتکل white-pages جهانی است. با این حال آنها می ‌توانند هر ورودی notaryHash را داخل mini-whitepages عکس‌ های خودشان اضافه یا به روز کنند.

function getUsers() constant returns (address[]) { return usersByAddress; }

  function getUser(address userAddress) constant returns (string,bytes32,bytes32,bytes32,bytes32[]) {
    return (Users[userAddress].handle,Users[userAddress].city,Users[userAddress].state,Users[userAddress].country,Users[userAddress].myImages);
  }

  function getAllImages() constant returns (bytes32[]) { return imagesByNotaryHash; }

  function getUserImages(address userAddress) constant returns (bytes32[]) { return Users[userAddress].myImages; }

  function getImage(bytes32 SHA256notaryHash) constant returns (string,uint) {
    return (notarizedImages[SHA256notaryHash].imageURL,notarizedImages[SHA256notaryHash].timeStamp);
  }

در نهایت، توابع جانبی فوق به ما اجازه می دهد به راحتی هر کاربر و یا تصویری را بخوانیم یا یک لیست white-pages کامل از تمام کاربران و عکس‌ ها داشته باشیم. در نظر داشته باشید که این توابع داده ‌هایی ثابت را بر می‌ گردانند، که یعنی آنها فقط قابل خواندن هستند، نه «state-changing». همچنین، رایگان فراخوانی می ‌شوند و فورا اطلاعات را تحویل می ‌دهند و نیازی نیست منتظر استخراج بلوک بعدی بمانید. فقط کافی ست نگاشت جهانی یا آرایه ‌های خود را فراخوانی کنیم، داده های مربوط به ما تحویل داده می‌ شود که شامل آدرس یا تصاویر کاربران توسط notaryHash می ‌باشد.

تدوین و تست یک قرارداد هوشمند

حال می‌ خواهیم قرارداد هوشمند را در یک محیط آزمایشی مورد ارزیابی واقعی قرار دهیم. استفاده از دستورات Truffle بسیار راحت است اما در ابتدا به یک «نود اتریوم محلی» برای ارزیابی نیازمندیم. اینجا همان جاییست که Ethereum TestRPC وارد میدان می‌ شود. TestPRC اساسا یک نود جعلی است؛ یک برنامه حاشیه‌ای (slim program) که فقط وانمود می ‌کند یک نود است و در ماشین localhost (میزبان محلی) شما شبیه به یک نود پاسخ می ‌دهد. TestRPC روی پورت 8545 مانند یک نود اتریوم معمولی اجرا می‌ شود و توانایی کامپایل کردن قراداد هوشمند Solidity در کدِ EVM و همچنین اجرا کردن کد را دارد. به علاوه، می ‌توانید هنگام آزمایش، پاسخ ‌های سریع دریافت کنید- به جای اینکه مجبور باشید در شبکه اتریوم واقعی برای استخراج بلوک بعدی منتظر بمانید.

آموزش گام به گام زبان برنامه نویسی Solidity برای ساخت DApp اتریوم

حال می‌توان دستورات کامپایل یا گسترش تستِ Truffle را به جای یک نود واقعی اتریوم اجرا کرد، اما به خاطر داشته باشید که این کار به اندازه ‌ی Ether واقعی هزینه در بر دارد. همچنین، برای اجرا کردن نود خودتان حتی در صورتی که نیازی به آن نداشته باشید، باز هم زمان اجرا و حافظه صرف می‌ شود. در ادامه چند دستور نصب سریع را ذکر می ‌کنیم:

npm i -g ethereum-testrpc

testrpc -m “sample dog come year spray crawl learn general detect silver jelly pilot”

TestRPC با عبارتِ «seed»ِ مشخص شده راه اندازی می‌ شود و شما باید چنین چیزی را در خروجی ببینید:

EthereumJS TestRPC v3.0.5

Available Accounts
==================
(0) 0x0ac21f1a6fe22241ccd3af85477e5358ac5847c2
(1) 0xb0a36610de0912f2ee794d7f326acc4b3d4bc7bc
...
(9) 0x4c1cc45ef231158947639c1eabec5c5cb187401c

Private Keys
==================
(0) 91e639bd434790e1d4dc4dca95311375007617df501e8c9c250e6a001689f2c7
(1) afaeff0fc68439c4057b09ef1807aaf4e695294db57bd631ce0ddd2e8332eea7
...
(9) dcc51540372fa2cf808efd322c5e158ad5b0dbf330a809c79b540f553c6243d7

HD Wallet
==================
Mnemonic:      sample dog come year spray crawl learn general detect silver jelly pilot
Base HD Path:  m/44'/60'/0'/0/{account_index}

Listening on localhost:8545

با اجرای TestRPC، همچنان که قرارداد هوشمند توسط Truffle یا Web3.js بسط و ارتباط می‌ یابد، شاهد فعالیت‌ هایی در این پنجره خواهید بود. اکنون برای انجام تست آماده هستیم. لازم است file /migrations/2_deploy_contracts.js را به گونه ‌ای اصلاح کنید که نام قرارداد هوشمند شما Truffle را شامل شود تا بداند که باید آن را کامپایل (compile) و آماده کار (deploy) کند. دستور این است:

truffle compile

اگر همه چیز خوب پیش رود، پیام «saving artifact» را خواهید دید که بیانگر عدم وجود هرگونه خطایی است. در صورتی که قرارداد شما دارای خطا های سینتکس یا مسائل دیگر باشد، تفسیر پیغام خطای کامپایلری که خواهید دید احتمالا عجیب و خیلی سخت خواهد بود. اگر خطایی در مورد «stack size» دریافت کردید، احتمالا به این معنی است که متغیر های زیادی را وارد تابع فراخوانی و یا از آن خارج کرده‌اید. یک روش خوب این است که به خاطر داشته باشید حداکثر ۱۶ متغیر ورودی یا خروجی می‌توان به کار برد. همچنین، به یاد داشته باشید که قرارداد های هوشمند Solidarity نمی ‌توانند داده ‌های با ساختار مرسوم را بازگردانند، بنابراین شما در یک محدوده کار می ‌کنید. معمولا من فقط آرایه ‌ها و اشاره‌ گر ها یا آدرس‌ دیگر ساختار ها را در نگاشت ‌های داخلی خود بر می ‌گردانم. اگر پیام خطایی در زمان اجرا در مورد «stack» دریافت کردید، ممکن است به این معنی باشد که کد شما در شرایط بدی است.

truffle migrate

فرمان (بالا) در واقع کارِ راه ‌اندازی تست قرارداد هوشمند شما را در نود TestRPC  انجام می ‌دهد. خروجی بدین صورت است:

Running migration: 1_initial_migration.js
  Deploying Migrations...
  Migrations: 0xd06a1935230c5bae8c7ecf75fbf4f17a04564ed8
Saving successful migration to network...
Saving artifacts...
Running migration: 2_deploy_contracts.js
  Deploying Geekt...
  Geekt: 0xe70ff0fa937a25d5dd4172318fa1593baba5a027
Saving successful migration to network...
Saving artifacts..

قرارداد هوشمند ما (که Geekt نام دارد) زمانی که با موفقیت راه اندازی شود، یک آدرس در بلاک چین اتریوم می ‌دهد. همانطور که می توانید در بالا ببینید، آدرس 0xe70ff0fa937a25d5dd4172318fa1593baba5a027 است. در شبکه اتریوم فعلی، شما  gas پرداخت می ‌کنید تا قراردادتان را اجرایی کنید و آدرس شما هیچ وقت تغییر نمی‌ کند. در TestRPC، اگر این گزینه را خاموش کنید، همه چیز فراموش خواهد شد و هنگامی که دوباره شروع به پشتیبانی TestRPC می‌ کنید، باید دوباره قرارداد خود را راه ‌اندازی کنید. در این صورت، آدرس متفاوتی برای قرارداد هوشمند خود دریافت خواهید کرد. آدرس قرارداد هوشمند شما جاییست که دیگران می توانند تراکنش‌ ها را به وسیله پیام با شما مبادله کنند تا تراکنش‌ های state-changing انجام دهند یا فقط داده ‌ها را به واسطه بلاک چین اتریوم بخوانند. قرارداد های هوشمندی که با سایر این گونه قرارداد ها در store در تعامل هستند، تغییرات یا اطلاعات را از طریق «پیام های» بلاک چین اتریوم که به عنوان سازمان های خودمختار غیر متمرکز یا DAO شناخته می ‌شوند، مبادله می ‌کنند.

آموزش گام به گام زبان برنامه نویسی Solidity برای ساخت DApp اتریوم

حال به  بخش سرگرم کننده می‌ رسیم. اکنون آماده‌ ایم تا آزمایش ‌های اولیه را انجام دهیم و با قرارداد هوشمندمان ارتباط برقرار کنیم. «console» را در Truffle راه ‌اندازی می‌ کنیم و نود  TestRPC localhost را به چالش می‌ کشیم تا مطمئن شویم همه چیز درست کار می ‌کند و می توانیم کاربران و تصاویر را اضافه کرده و آنها را به درستی بازیابی کنیم.

truffle console
> Geekt = Geekt.deployed()
> Geekt.then(function(instance){return JSON.stringify(instance.abi);})
> Geekt.then(function(instance){return instance.registerNewUser("Tectract","Denver","CO","USA");})

> Geekt.then(function(instance){return instance.addImageToUser('www.myimageURL.com','0x6c3e007e281f6948b37c511a11e43c8026d2a16a8a45fed4e83379b66b0ab927');})

> Geekt.then(function(instance){return instance.getUser('0x0ac21f1a6fe22241ccd3af85477e5358ac5847c2');}) > Geekt.then(function(instance){return instance.getUsers();})
> Geekt.then(function(instance){return instance.getImages();})
> Geekt.then(function(instance){return instance.get

یک مفهوم مهم که در اینجا مطرح می شود ABI است؛ یک شیء با فرمت جاوا اسکریپت که نام توابع و ورودی ‌ها یا  خروجی‌ ها را برای تعامل با قرارداد هوشمند شما توصیف می ‌کند. ABI به نحوی شبیه تعریف API (واسط برنامه و برنامه نویس) است، که برای قرارداد هوشمند طراحی شده و به مردم می ‌گوید که چگونه پیام ‌هایشان را شکل دهند.

آموزش گام به گام زبان برنامه نویسی Solidity برای ساخت DApp اتریوم

تابع MyregisterNewUser() شروع به کار می ‌کند. در پاسخ به فراخوانی تابع registerNewUser() در پنجره console مشاهده می‌ کنیم:

{ tx: '0x1b9f55971871921ccd23a9aa7620620c6c958a893af334087283926d4c6d60b1',
  receipt:
   { transactionHash: '0x1b9f55971871921ccd23a9aa7620620c6c958a893af334087283926d4c6d60b1',
     transactionIndex: 0,
     blockHash: '0x2be4fab68daaf8db199e2a6adea101c0f1ed06f46aba21e8e4c06e752ca3325c',
     blockNumber: 5,
     gasUsed: 145215,
     cumulativeGasUsed: 145215,
     contractAddress: null,
     logs: [] },
  logs: [] }

تابع AddImageToUser() به طور مشابه با موفقیت باز می ‌گردد و حالا می‌توانیم سوابق فردی کاربر یا پرونده های تصویری ثبت شده را از بلاک چین اتریوم بازیابی کنیم.

فراخوانی، تابع getUser() بر می گرداند:

[ 'Tectract',
  '0x44656e7665720000000000000000000000000000000000000000000000000000',
  '0x434f000000000000000000000000000000000000000000000000000000000000',
  '0x5553410000000000000000000000000000000000000000000000000000000000',
  [ '0x6c3e007e281f6948b37c511a11e43c8026d2a16a8a45fed4e83379b66b0ab927' ] ]
and I can grab that image data via the 32-byte SHA256Notary hash string I have associated with it, my getImage() function returns:

[ 'www.myimageURL.com',
  { [String: '1496256315'] s: 1, e: 9, c: [ 14962 ] } ]

همه چیز به خوبی کار می ‌کند، تست در TestRPC و Truffle انجام شده، و حالا آماده هستیم تا به طور حقیقی یک رابط کاربر گرافیکی برای Dapp خودمان بسازیم.

طراحی Dapp اتریوم پایه:

طراحی و آزمایش localhost:

تا کنون، قرارداد هوشمند زبان برنامه نویسی Solidity خود را با موفقیت کمپایل و در نود محلی اتریوم testRPC راه اندازی کردیم. در حال حاضر می‌ توانیم به سرعت یک Dapp ساده ایجاد کنیم که به کاربران امکان می‌ دهد از طریق مرورگر وب خود با قرارداد هوشمند ارتباط برقرار کنند. از این پس از برخی برنامه نویسی پایه وب و ماژول ویژه جاوا اسکریپت Web3.js، که به طور خاص برای ارتباط با نود های اتریوم و قرارداد های هوشمند تحت وب ساخته شده اند، استفاده می ‌کنیم.

این نسخه نمایشی (open-source github repositories) به عنوان یک مرجع آماده شده است تا دیگران بتوانند از ان برای ساخت DApp های اتریوم به وسیله ی web3.js استفاده کنند. این نسخه‌ی نمایشی با یک ابزار کاربرد آسان  به نام Create-React-App ساخته شده است که از یک زبان وب داخلی واکنشی Facebook استفاده می‌ کند. ما نمی ‌خواهیم روی هیچ یک از کد های React تمرکز کنیم، فقط به دستورات جاوا اسکریپت web.js نیاز داریم. کد من در CommonJS است، تقریبا مشابه با جاوا اسکریپت ساده-قدیمی ES6.

بنابراین شما باید در برنامه وب خودتان، ماژول-نود web3 را به وسیله ‌ی یک دستور مانند «npm i -S web3» نصب کنید. اگر در حال استفاده از آن هستید، در package.json file شما ذخیره خواهد شد. بنابراین، داخل یک “” تگ یا داخل یک فایل .js، یک خط شبیه این را خواهید داشت که ماژول web3 خودش را بارگذاری می کند:

import Web3 from ‘web3’;

(or, in ES6 javascript)

var Web3 = require(‘web3’);

لازم است بعضی جزئیات را در مورد نود و قرارداد هوشمندمان به Web3 اطلاع دهیم، تا بتواند اتصال برقرار کند.

شما خطوطی شبیه به این خواهید دید:

# this line specificies our localhost node IP:port settings
var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));

# our smart-contract address, from above
var GeektAddress = '0xe70ff0fa937a25d5dd4172318fa1593baba5a027';

# our smart-contract ABI, as raw javascript object, not quoted string!
var GeektABI = [{"constant":true,"inputs":[],"name":"getUsers","outputs":[{"name":"",   (...)

# this loads the smart-contract into our web3 object
GeektContract = web3.eth.contract(GeektABI).at(GeektAddress);

# this finds the default account listed for the node connection (account 0 from TestRPC above)
defaultAccount = web3.eth.accounts[0];
Here are some lines that demonstrate read-only functions interfacing with our smart-contract, via web3:

# the below function is activated when the page loads, to get the "white-pages" of all known user addresses
GeektContract.getUsers(function(err,usersResult){

# the below function grabs an image as needed, and stores them in browser memory...
GeektContract.getImage(imageHash,function(err,imageRe

در این نسخه ‌ی نمایشی ساده Dapp فقط آدرس‌ تمام کاربر های شناخته شده بارگذاری می ‌شود؛ جزئیات واقعی تصویر کاربر واقعی را بر می دارد و تصویر را به عنوان on-the-fly ثبت می‌ کند. اسکرین شات زیر را ببینید:

آموزش گام به گام زبان برنامه نویسی Solidity برای ساخت DApp اتریوم

در این مرحله (تصویر بالا) من TestRPC  مشتری‌ ام را refresh کرده‌ ام و فقط دستور «truffle migrate» را انجام داده‌ ام که قرارداد هوشمند را با نود localhost TestRPC راه‌ اندازی می‌ کند. می‌ بینید که رابط کاربری برای ثبت نام و کلیک دکمه «register» آماده است. وقتی این کلیک انجام می ‌شود تابع registerNewUser() برای قرارداد هوشمند فراخوانی خواهد شد و داده ‌های کاربر (data-user) را به نود بلاک چین اتریوم من (تست localhost) در آدرس قرارداد هوشمندم که در بالا اشاره شد، اضافه می‌ کند. اضافه کردن داده-کاربر به حافظه بلاک چین، هزینه ‌ی gas را در پی دارد و باید محاسبه کنیم چقدر gas باید پرداخت کنیم.

در اینجا کدی داریم که وقتی کلید «Sign Guestbook» را کلیک می کنیم، فراخوانی می شود.

GeektContract.registerNewUser.estimateGas(
outerThis.state.defaultHandle,
outerThis.state.defaultCity,
outerThis.state.defaultState,
outerThis.state.defaultCountry,
{from:defaultAccount},
function(err, result){

...

  var myGasNum = result;
  GeektContract.registerNewUser.sendTransaction(
  outerThis.state.defaultHandle,
  outerThis.state.defaultCity,
  outerThis.state.defaultState,
  outerThis.state.defaultCountry,
  {from:defaultAccount, gas: myGasNum},
  function(err, result){

بنابراین همانطور که می ‌بینید، وقتی یک تراکنش state-changing انجام می ‌دهیم، مجبوریم gas اتریوم  بپردازیم (gas واقعی یا آزمایشگاهی). تابع registerNewUser.estimateGas() را پیشاپیش فرا می ‌خوانیم و سپس وقتی که تابع state-changing را با استفاده از   registerNewUser.sendTransaction() فرا خواندیم از مقدار gas تخمین زده شده  استفاده می‌ کنیم.

وقتی که ما TestRPC را شروع کردیم و تعدادی آدرس‌ کیف پول تستی دریافت کردیم، این حساب‌ ها به حسابی که یک نود کامل را اجرا کردید و دستور getAccounts RPC همراه آن انجام دادید (شبیه یک کیف پول فول نود)، وابسته هستند. TestRPC هنگام راه اندازی حدود ۱۰۰ واحد testnet Ether آزمایشی به هر اکانت می دهد.

وقتی توابع state-changing، مانند registerNewUser() یا addImageToUser() را فرا می‌ خوانیم، در مرحله localhost testing، همراه با نود TestRPC، TestRPC فقط testnet gas را می‌ پردازد و نتیجه تراکنش را فورا برمی گرداند. در یک فول نود localhost، بایستی حساب را قبل از اینکه تراکنش در mainnet موفقیت آمیز باشد، « unlock»کنید. با فرض اینکه gas تخمینی کافی است باید در mainnet منتظر بلوک بعدی بمانید تا تراکنش‌ تان انجام شود. اما همیشه راه ‌های بهتری هم هست.

آموزش گام به گام زبان برنامه نویسی Solidity برای ساخت DApp اتریوم

اتصال DApp به mainnet اتریوم

من قرار داد هوشمند را در یک mainnet اتریوم نصب و راه اندازی کردم و برنامه نسخه ‌ی نمایشی از اینجا قابل دسترس است. اگر از این سایت بازدید کنید و یک نود TestRPC یا اتریوم کامل که در localhost:8545 برای اجرا نداشته باشید، چیزی شبیه به این پیغام مشاهده خواهید کرد:

Saw connection to network: !

Saw default account: (no web3.eth node link)

آیا این بدین معنی است که شما نیاز به اجرای یک نود کامل اتریوم یا یک نود کامل سرور خصوصی مجازی (VPS) برای اتصال و ارتباط با Mainnet اتریوم دارید؟ تا همین اواخر این مساله درست بود، اما اکنون ما پلاگینِ عالیِ Metamask (برای مرورگر کروم) را داریم که اجازه می دهد از طریق مرورگر خود به Mainnet اتریوم متصل شوید. Metamask اساسا اتصال به یک نود کامل را برای شما فراهم و به صورت رایگان از آن‌ ها پشتیبانی می ‌کند.

آموزش گام به گام زبان برنامه نویسی Solidity برای ساخت DApp اتریوم

می‌ توانید «injected» اختصاصی web3 از پلاگین metamask که نود را به فول نود اتریوم متصل می‌ کند، در کد من مشاهده کنید. کدی که به طور خودکار به مشتری معمول شما (localhost) سوئیچ می‌ کند، و یا اتصال metamask web3 را مشاهده می ‌کنید:

function loadWeb3() {
  let web3Injected = window.web3;
  if(typeof web3Injected !== 'undefined'){
    console.log("saw injected web3!");
    web3 = new Web3(web3Injected.currentProvider);
  } else {
    console.log("did not see web3 injected!");
    web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
  }
}

در تصویر زیر، لوگوی Metamask Fox در گوشه سمت راست بالا به نمایش درآمده است. من پلاگین  Metamask را نصب کرده و به اتریوم Mainnet متصل شدم. می توانید ببینید که یک جفت کاربر را در دموی Guestbook قرارداد هوشمند «Geekt» اضافه کرده‌ ام. رابط کاربری DApp به شما این امکان را می‌ دهد تا کاربری را انتخاب و تصاویر او را مرور کنید. اگر از یک آدرس که کاربرش هنوز ثبت نشده متصل شوید، صفحه ورود به سیستم مهمان Sign Guestbook را مشاهده خواهید کرد.

من کد های دیگری نیز اضافه کرده‌ام، بنابراین شما فقط می‌توانید در URL تصویری که می‌ خواهید به حساب کاربری خود اضافه کنید را وارد نمایید. سپس SHA256 notary hash به تصویر شما اضافه می ‌شود. همچنین، SHA256 notary hashes تصاویری را که داخل صفحه لود شده است را چک خواهد کرد و یک علامت سبز کوچک یا یک «X» قرمز بر این اساس که آیا تصویر در URL و notary hashes مطابقت دارند یا خیر، به شما می ‌دهد.

برای اضافه کردن تصویر به یک حساب کاربری، پلاگین Metamask اعلام می‌ کند که به پرداخت gas واقعی اتریوم برای تراکنش Ethereum web3 نیاز دارم. پنجره ‌ای کوچک به نمایش در می‌ آید که آیا چنین تراکنشی را تایید می‌کنید؟ من این تراکنش را قبول کردم و یک اتر واقعی به کیف پول Metamask فرستادم که برای این تراکنش کافی بود.

آموزش گام به گام زبان برنامه نویسی Solidity برای ساخت DApp اتریوم

سخنان نهایی

این مقاله باید تا حد زیادی به شما کمک کند تا نسخه آزمایشی و کد های مربوط به زبان برنامه نویسی solidity اتریوم را به خوبی آموخته و یک توسعه دهنده DApp شوید. می توانید پلاگین Metamask را نصب کنید، به MAinnet اتریوم متصل شوید و به لینک www.enledger.io/etherereum-demo-tools مراجعه کنید. در آنجا برنامه ي Guestbook / Notary Demo را مشاهده خواهید کرد و حتی اگر بخواهید می توانید guestbook و عکس های notarize را هم امضا کنید.

برای کنترل بیشتر روی قرارداد هوشمند، توانایی های بیشتری برای «صاحب» این قرارداد در نظر می گیرم؛ که بتواند آدرس اولین پرداخت کننده برای راه اندازی قرارداد هوشمند در mainnet اتریوم را، که در اینجا خودم هستم، ببیند. با این کار قصد دارم خیلی سریع به شما نشان دهم که ادمین خاص چه توانایی هایی دارد:

address GeektAdmin;
  function Geekt() payable {  // this is the CONSTRUCTOR (same name as contract) it gets called ONCE only when contract is first deployed
    GeektAdmin = msg.sender;  // just set the admin, so they can remove bad users or images if needed, but nobody else can
  }
  modifier onlyAdmin() {
      if (msg.sender != GeektAdmin)
        throw;
      // Do not forget the "_;"! It will be replaced by the actual function body when the modifier is used.
      _;
  }
  function removeUser(address badUser) onlyAdmin returns (bool success) {
    delete Users[badUser];
    return true;
  }
  function removeImage(bytes32 badImage) onlyAdmin returns (bool success) {
    delete notarizedImages[badImage];
    return true;
  }

تابع سازنده خاص Geekt() را هم نام قرارداد ساختیم. این تابع تنها یکبار، زمان اولین فراخوانی قرارداد روی بلاک چین، فراخوانی می شود. این تابع، آدرسِ msg.sender شخصی که اولین پرداخت برای راه اندازی قرارداد هوشمند را انجام داده، به ادمین نشان می دهد. همچنین، تابع اصلاح کننده خاص onlyAdmin() برای محدود کردن توابع removeUser() و removeImage() به کار می رود و این توابع فقط زمانی فعال می شوند که آدرس msg.sender آدرسِ ادمین باشد.

در این مقاله فقط نوع خاصی از قرارداد هوشمند را پوشش داده ایم. انواع دیگری از قرارداد هوشمند می توانند وجود داشته باشند. امیدواریم این آموزش انگیزه کافی برای یادگیری نوشتن هرگونه قرارداد هوشمند را به شما بدهد. قطعا هنوز چیز های بسیاری برای آموختن وجود دارد.

نکته نهایی دیگر، ساختارِ اتریوم و هزینه واقعی استفاده از قرارداد های هوشمند است. اتریوم شبکه ایست که روی یک بلاک چین عمومی، منفرد و بزرگ اجرا می شود و هرکسی می تواند داده های آن را ارزیابی و ذخیره کند. با این حال، انجام چنین کاری تا حدودی گران است. همانطور که در تصویر فوق می بینید، من ۰.۰۰۴۱۸۲ اتر که معادل ۰.۹۶ دلار است برای ذخیره یک عکس URL، SHA256 notary hash، زماندار و ۱۹۶ بایتی روی Mainnet اتریوم، پرداخت کردم. یعنی، ۵۱۵۹.۰۳ دلار برای یک مگابایت! (ارقام بر اساس زمان نگارش مقاله آورده شده است)

همانطور که پیش تر گفتیم، ایده ای که برای هزینه gas در وایت پیپر اتریوم شرح داده شده این است که در حالت ایده آل هزینه باید تقریبا ثابت باشد. هرچند، هزینه gas در زمان اجرای واقعی به تعداد بلوک ها بستگی دارد و تعداد بلوک ها در آینده ی نزدیک به قیمت بازار اتریوم نمی رسد که باعث می شود gas در شرایط واقعی گران تر باشد. همچنین، هارد فورک ها نشان داده اند که بلاک چین اتریوم واقعا یک زنجیره عمومی است و در زمان وقوع هارد فورک، از لحاظ تئوری ممکن است دیتای شما از بین برود یا قیمت دارایی پایه به شدت کاهش یابد.

ممکن است اتریوم برای چیز هایی که فضای عمومی کمی برای ذخیره سازی روی بلاک چین نیاز دارند، مثلِ دفتر اسناد رسمی آنلاین، مناسب باشد. اما ساختن یک پروژه بلاک چینی برای قرار دادن اطلاعات صنعتی روی بلاک چین اتریوم ممکن است منطقی نباشد، چرا که احتمالا نمی خواهید تمام این اطلاعات را در دسترس عموم قرار دهید و مجبور خواهید شد که برای کاربرد های خاص صنعتی کارمزد تراکنش بپردازید.

با این اوصاف، پیشنهاد می کنم پیش از شروع پروژه بلاک چینی جدید، به دقت پلتفرم مورد نظرتان را ارزیابی کنید. درمیان جدید ترین متد ها جستجو کنید و از کشف کردن نهراسید. به مردم انگیزه بدهید تا نود شما را دانلود و اجرا کنند. بهترین مکمل برنامه نویس این است که مردم از کد در دنیای واقعی استفاده کنند.

نکات سری!

فرض کنید قرارداد هوشمند خود را نوشتید، هزار بار تست می کنید تا مطمئن شوید تمام بخش ها به خوبی کار می کند. حال چطور آن را درون Mainnet  قرار می دهید؟

این کار مشکل است و معمولا نیاز پیدا می کنید که فول نود اتریوم را خودتان اجرا کنید یا پرداختی انجام دهید و کس دیگری این کار را برای شما انجام دهد و به شما اجازه ارزیابی بدهد (مثلا با VPS). برای اولین بار، می توانید قرارداد هوشمند کامل خود را در Mainnet اتریوم، بدون نیاز به فول نود اتریوم خودتان، تست و حتی اجرا کنید. ابزار EthDeployer نرم افزار رایگانیست که به همین منظور ساخته شده است (از اینجا نیز قابل دسترس است). کافیست پلاگین Metamask را نصب کنید و ابتدا به Mainnet اتریوم متصل شوید. به علاوه نیاز دارید مقداری اتر در کیف پول Metamask خود برای پرداخت کارمزد راه اندازی قرارداد هوشمند Mainnet داشته باشید.

این ابزار از Browser-Solc استفاده می کند که فقط یک browserified، ابزار بارگیری minified برای بارگذاری ورژن Solidity کامپایلرِ جاوا اسکریپت، مستقیما در مرورگر سرویس گیرنده، برای تجزیه و تحلیل یا کامپایل یک قرارداد هوشمند on-the-fly، است. این ویژگی باعث می شود این ابزار پرتابل باشد. کد زیر آخرین کامپایلر  Solidity موجود را بارگذاری می کند:

<script src=”./browser-solc.min.js” type=”text/javascript”></script>

browser-solc.min.j جاوا اسکریپ را با صفحه سطح بالای index.html بارگذاری می کنیم که شیء window.BrowserSolc را برای عکس العمل اسکریپت های سطح پایین، می سازد. این برنامه بسیار ساده است و در عرض چند دقیقه روی دستگاه شما نصب می شود.

setupCompiler(){
    var outerThis = this;
    setTimeout(function(){
      // console.debug(window.BrowserSolc);
      window.BrowserSolc.getVersions(function(soljsonSources, soljsonReleases) {
        var compilerVersion = soljsonReleases[_.keys(soljsonReleases)[0]];
        console.log("Browser-solc compiler version : " + compilerVersion);
        window.BrowserSolc.loadVersion(compilerVersion, function(c) {
          compiler = c;
          outerThis.setState({statusMessage:"ready!"},function(){
            console.log("Solc Version Loaded: " + compilerVersion);
          });
        });
      });
    },1000);
  }

تابع setupCompiler پس از یک ثانیه انتظار برای شیء window.BrowserSolc روی صفحه بارگذاری می گردد. سپس توابع داخلی .getVersions() و .loadVersion() و کامپایلر تابعی Solc مستقیما در محیط مشتری اجرا می شوند. برای تکمیل، خطوط مربوط به اجرای واقعی و به کار گیری قرارداد درون یک تابع جاوا اسکریپت را می آورم:

compileAndDeploy() {
     ...
     var result = compiler.compile(this.state.contractText, optimize);
     var abi = JSON.parse(result.contracts[_.keys(result.contracts)[0]].interface);
     var bytecode = "0x" + result.contracts[_.keys(result.contracts)[0]].bytecode;
     var myContract = web3.eth.contract(abi);
     ...
       web3.eth.estimateGas({data: bytecode},function(err,gasEstimate){
          ...
          myContract.new({from:web3.eth.accounts[0],data:bytecode,gas:inflatedGasCost},function(err, newContract){
            ...
            thisTxHash: newContract.transactionHash,
            thisAddress: newContract.address

پیش تر با تمام این شیء ها آشنا شده ایم. compiler.compile() را روی متن قرارداد فراخوانی کرده، شیء «نتیجه» قرارداد اجرا شده را از abi و bytecode استخراج می کنیم و به یک تراکنش جدید می فرستیم. تابع .estimateGas() برای انجام تخمین خوب، اینجا قرار داده شده است. وقتی قرارداد با موفقیت اجرا می شود، شناسه تراکنش و آدرس قرارداد جدید را به کاربر نشان می دهیم.اگر هنگام اجرا با پیغام خطا مواجه شدید، حتما لینک شناسه تراکنش را چک کنید. احتمالا قرارداد به طور موفقیت آمیز در حال اجراست؛ گاهی Metamask در انتخاب تراکنش موفق و برداشتن آدرس جدید دچار اشکال می شود.در نهایت، من ۰.۰۲۲۶۸ اتر و معادل ۵.۳۰ دلار در تاریخ ۱ ژوئن ۲۰۱۷ برای راه اندازی قرارداد در Mainnet اتریوم پرداخت کردم. به خاطر آورید که پیش نمایش خیلی ساده اپلیکیشن با تنها دو ساختار داده مد نظرمان بود. بنابراین اصلا مقرون به صرفه نیست. هرچند، شما اغلب تنها یکبار نیاز دارید که قرارداد هوشمندتان را در زنجیره اصلی اجرا کنید.

توجه!

دیدگاه هایی که در این مقاله ارائه شده اند، متعلق به نویسنده می باشند و لزوماً مربوط به Coiniran نمی باشد و نباید به آن نسبت داده شود.

سخنان نهایی: این مقاله باید تا حد زیادی به شما کمک کند تا نسخه آزمایشی و کد های مربوط به زبان برنامه نویسی solidity اتریوم را به خوبی آموخته و یک توسعه دهنده DApp شوید. می توانید پلاگین Metamask را نصب کنید، به MAinnet اتریوم متصل شوید و به لینک www.enledger.io/etherereum-demo-tools مراجعه کنید. در آنجا برنامه ي Guestbook / Notary Demo را مشاهده خواهید کرد و حتی اگر بخواهید می توانید guestbook و عکس های notarize را هم امضا کنید.

برای کنترل بیشتر روی قرارداد هوشمند، توانایی های بیشتری برای «صاحب» این قرارداد در نظر می گیرم؛ که بتواند آدرس اولین پرداخت کننده برای راه اندازی قرارداد هوشمند در mainnet اتریوم را، که در اینجا خودم هستم، ببیند. با این کار قصد دارم خیلی سریع به شما نشان دهم که ادمین خاص چه توانایی هایی دارد:

address GeektAdmin;
  function Geekt() payable {  // this is the CONSTRUCTOR (same name as contract) it gets called ONCE only when contract is first deployed
    GeektAdmin = msg.sender;  // just set the admin, so they can remove bad users or images if needed, but nobody else can
  }
  modifier onlyAdmin() {
      if (msg.sender != GeektAdmin)
        throw;
      // Do not forget the "_;"! It will be replaced by the actual function body when the modifier is used.
      _;
  }
  function removeUser(address badUser) onlyAdmin returns (bool success) {
    delete Users[badUser];
    return true;
  }
  function removeImage(bytes32 badImage) onlyAdmin returns (bool success) {
    delete notarizedImages[badImage];
    return true;
  }

تابع سازنده خاص Geekt() را هم نام قرارداد ساختیم. این تابع تنها یکبار، زمان اولین فراخوانی قرارداد روی بلاک چین، فراخوانی می شود. این تابع، آدرسِ msg.sender شخصی که اولین پرداخت برای راه اندازی قرارداد هوشمند را انجام داده، به ادمین نشان می دهد. همچنین، تابع اصلاح کننده خاص onlyAdmin() برای محدود کردن توابع removeUser() و removeImage() به کار می رود و این توابع فقط زمانی فعال می شوند که آدرس msg.sender آدرسِ ادمین باشد.

در این مقاله فقط نوع خاصی از قرارداد هوشمند را پوشش داده ایم. انواع دیگری از قرارداد هوشمند می توانند وجود داشته باشند. امیدواریم این آموزش انگیزه کافی برای یادگیری نوشتن هرگونه قرارداد هوشمند را به شما بدهد. قطعا هنوز چیز های بسیاری برای آموختن وجود دارد.

آموزش گام به گام زبان برنامه نویسی Solidity برای ساخت DApp اتریوم

نکته نهایی دیگر، ساختارِ اتریوم و هزینه واقعی استفاده از قرارداد های هوشمند است. اتریوم شبکه ایست که روی یک بلاک چین عمومی، منفرد و بزرگ اجرا می شود و هرکسی می تواند داده های آن را ارزیابی و ذخیره کند. با این حال، انجام چنین کاری تا حدودی گران است. همانطور که در تصویر فوق می بینید، من ۰.۰۰۴۱۸۲ اتر که معادل ۰.۹۶ دلار است برای ذخیره یک عکس URL، SHA256 notary hash، زماندار و ۱۹۶ بایتی روی Mainnet اتریوم، پرداخت کردم. یعنی، ۵۱۵۹.۰۳ دلار برای یک مگابایت! (ارقام بر اساس زمان نگارش مقاله آورده شده است)

همانطور که پیش تر گفتیم، ایده ای که برای هزینه gas در وایت پیپر اتریوم شرح داده شده این است که در حالت ایده آل هزینه باید تقریبا ثابت باشد. هرچند، هزینه gas در زمان اجرای واقعی به تعداد بلوک ها بستگی دارد و تعداد بلوک ها در آینده ی نزدیک به قیمت بازار اتریوم نمی رسد که باعث می شود gas در شرایط واقعی گران تر باشد. همچنین، هارد فورک ها نشان داده اند که بلاک چین اتریوم واقعا یک زنجیره عمومی است و در زمان وقوع هارد فورک، از لحاظ تئوری ممکن است دیتای شما از بین برود یا قیمت دارایی پایه به شدت کاهش یابد.

بیشتر بخوانید: سرنوشت توکن ها در ۲۰۱۸

ممکن است اتریوم برای چیز هایی که فضای عمومی کمی برای ذخیره سازی روی بلاک چین نیاز دارند، مثلِ دفتر اسناد رسمی آنلاین، مناسب باشد. اما ساختن یک پروژه بلاک چینی برای قرار دادن اطلاعات صنعتی روی بلاک چین اتریوم ممکن است منطقی نباشد، چرا که احتمالا نمی خواهید تمام این اطلاعات را در دسترس عموم قرار دهید و مجبور خواهید شد که برای کاربرد های خاص صنعتی کارمزد تراکنش بپردازید.

با این اوصاف، پیشنهاد می کنم پیش از شروع پروژه بلاک چینی جدید، به دقت پلتفرم مورد نظرتان را ارزیابی کنید. درمیان جدید ترین متد ها جستجو کنید و از کشف کردن نهراسید. به مردم انگیزه بدهید تا نود شما را دانلود و اجرا کنند. بهترین مکمل برنامه نویس این است که مردم از کد در دنیای واقعی استفاده کنند.

آموزش گام به گام زبان برنامه نویسی Solidity برای ساخت DApp اتریوم

نکات سری!

فرض کنید قرارداد هوشمند خود را با زبان برنامه نویسی solidity نوشتید، هزار بار تست می کنید تا مطمئن شوید تمام بخش ها به خوبی کار می کند. حال چطور آن را درون Mainnet  قرار می دهید؟

این کار مشکل است و معمولا نیاز پیدا می کنید که فول نود اتریوم را خودتان اجرا کنید یا پرداختی انجام دهید و کس دیگری این کار را برای شما انجام دهد و به شما اجازه ارزیابی بدهد (مثلا با VPS). برای اولین بار، می توانید قرارداد هوشمند کامل خود را در Mainnet اتریوم، بدون نیاز به فول نود اتریوم خودتان، تست و حتی اجرا کنید. ابزار EthDeployer نرم افزار رایگانیست که به همین منظور ساخته شده است (از اینجا نیز قابل دسترس است). کافیست پلاگین Metamask را نصب کنید و ابتدا به Mainnet اتریوم متصل شوید. به علاوه نیاز دارید مقداری اتر در کیف پول Metamask خود برای پرداخت کارمزد راه اندازی قرارداد هوشمند Mainnet داشته باشید.

آموزش گام به گام زبان برنامه نویسی Solidity برای ساخت DApp اتریوم

این ابزار از Browser-Solc استفاده می کند که فقط یک browserified، ابزار بارگیری minified برای بارگذاری ورژن Solidity کامپایلرِ جاوا اسکریپت، مستقیما در مرورگر سرویس گیرنده، برای تجزیه و تحلیل یا کامپایل یک قرارداد هوشمند on-the-fly، است. این ویژگی باعث می شود این ابزار پرتابل باشد. کد زیر آخرین کامپایلر  Solidity موجود را بارگذاری می کند:

<script src=”./browser-solc.min.js” type=”text/javascript”></script>

browser-solc.min.j جاوا اسکریپ را با صفحه سطح بالای index.html بارگذاری می کنیم که شیء window.BrowserSolc را برای عکس العمل اسکریپت های سطح پایین، می سازد. این برنامه بسیار ساده است و در عرض چند دقیقه روی دستگاه شما نصب می شود.

setupCompiler(){
    var outerThis = this;
    setTimeout(function(){
      // console.debug(window.BrowserSolc);
      window.BrowserSolc.getVersions(function(soljsonSources, soljsonReleases) {
        var compilerVersion = soljsonReleases[_.keys(soljsonReleases)[0]];
        console.log("Browser-solc compiler version : " + compilerVersion);
        window.BrowserSolc.loadVersion(compilerVersion, function(c) {
          compiler = c;
          outerThis.setState({statusMessage:"ready!"},function(){
            console.log("Solc Version Loaded: " + compilerVersion);
          });
        });
      });
    },1000);
  }

تابع setupCompiler پس از یک ثانیه انتظار برای شیء window.BrowserSolc روی صفحه بارگذاری می گردد. سپس توابع داخلی .getVersions() و .loadVersion() و کامپایلر تابعی Solc مستقیما در محیط مشتری اجرا می شوند. برای تکمیل، خطوط مربوط به اجرای واقعی و به کار گیری قرارداد درون یک تابع جاوا اسکریپت را می آورم:

compileAndDeploy() {
     ...
     var result = compiler.compile(this.state.contractText, optimize);
     var abi = JSON.parse(result.contracts[_.keys(result.contracts)[0]].interface);
     var bytecode = "0x" + result.contracts[_.keys(result.contracts)[0]].bytecode;
     var myContract = web3.eth.contract(abi);
     ...
       web3.eth.estimateGas({data: bytecode},function(err,gasEstimate){
          ...
          myContract.new({from:web3.eth.accounts[0],data:bytecode,gas:inflatedGasCost},function(err, newContract){
            ...
            thisTxHash: newContract.transactionHash,
            thisAddress: newContract.address

پیش تر با تمام این شیء ها آشنا شده ایم. compiler.compile() را روی متن قرارداد فراخوانی کرده، شیء «نتیجه» قرارداد اجرا شده را از abi و bytecode استخراج می کنیم و به یک تراکنش جدید می فرستیم. تابع .estimateGas() برای انجام تخمین خوب، اینجا قرار داده شده است. وقتی قرارداد با موفقیت اجرا می شود، شناسه تراکنش و آدرس قرارداد جدید را به کاربر نشان می دهیم.اگر هنگام اجرا با پیغام خطا مواجه شدید، حتما لینک شناسه تراکنش را چک کنید. احتمالا قرارداد به طور موفقیت آمیز در حال اجراست؛ گاهی Metamask در انتخاب تراکنش موفق و برداشتن آدرس جدید دچار اشکال می شود.در نهایت، من ۰.۰۲۲۶۸ اتر و معادل ۵.۳۰ دلار در تاریخ ۱ ژوئن ۲۰۱۷ برای راه اندازی قرارداد در Mainnet اتریوم پرداخت کردم. به خاطر آورید که پیش نمایش خیلی ساده اپلیکیشن با تنها دو ساختار داده مد نظرمان بود. بنابراین اصلا مقرون به صرفه نیست. هرچند، شما اغلب تنها یکبار نیاز دارید که قرارداد هوشمندتان را در زنجیره اصلی اجرا کنید.

توجه!

دیدگاه هایی که در این مقاله ارائه شده اند، متعلق به نویسنده می باشند و لزوماً مربوط به Coiniran نمی باشد و نباید به آن نسبت داده شود.