สัญญา Ethereum Oracle: การตั้งค่าและการวางแนว

เผยแพร่แล้ว: 2022-03-11

Ethereum Smart Contracts เป็นมากกว่า "สิ่งใหม่ที่น่าสนใจ" ฉันเชื่อว่าพวกเขา (หรือสิ่งที่เกี่ยวข้อง) พร้อมที่จะเปลี่ยนวิธีที่มนุษย์ทำธุรกิจร่วมกันในยุคอินเทอร์เน็ตยุคใหม่ที่จะมาถึง เวลาจะบอกได้ถ้าเป็นอย่างนั้น

นี่เป็นบทความสามส่วนแรกเกี่ยวกับการพัฒนาสัญญาอัจฉริยะของ Ethereum ด้วย Solidity โดยเฉพาะอย่างยิ่งการสำรวจการใช้สัญญาที่เรียกว่า "oracles" ซึ่งโดยทั่วไปแล้วจะเป็นสัญญาที่ปั๊มข้อมูลลงในบล็อกเชนเพื่อใช้โดยสัญญาอัจฉริยะอื่น ๆ

  • ตอนที่ 1: ความรู้เบื้องต้นเกี่ยวกับการพัฒนาเห็ดทรัฟเฟิล และการตั้งค่าโครงการสำหรับการทดลองต่อไป
  • ส่วนที่ 2: เจาะรหัสเพื่อการตรวจสอบที่ลึกซึ้งยิ่งขึ้น
  • ส่วนที่ 3: การอภิปรายเชิงแนวคิดของ oracles กับ smart contracts

เป้าหมายของเรื่องนี้ ในตอนที่ 1 ของซีรีส์นี้ ไม่ได้หมายความถึงแนวคิดของ oracle contracts มากนัก ปรัชญาเบื้องหลัง หรือแม้แต่อย่างลึกซึ้งในสิ่งที่พวกเขาเป็น เป้าหมายของส่วนนี้ของบทช่วยสอน Ethereum oracle ของเราคือ:

  • ให้คุณตั้งค่าด้วยการสร้างสัญญาอัจฉริยะกับทรัฟเฟิล
  • สร้างโครงการสัญญาอัจฉริยะที่จะให้บริการเราในส่วนที่ 2 และ 3
  • แนะนำแนวคิดบางประการที่เกี่ยวข้องกับสัญญาอัจฉริยะ Ethereum และการเข้ารหัสสัญญาอัจฉริยะ
  • แนะนำวงจรคอมไพล์/รัน/ดีบักด้วยทรัฟเฟิลและสัญญาอัจฉริยะ

คำนิยาม: ออราเคิล วิธีการสำหรับสัญญาอัจฉริยะในการเข้าถึงข้อมูลจากโลกภายนอกบล็อคเชน ประเภทของสัญญาอัจฉริยะเอง oracles นำข้อมูลจากโลกภายนอกและใส่ลงในบล็อกเชนเพื่อให้สัญญาอัจฉริยะอื่น ๆ ใช้

ส่วนแรกของบทความนี้จะประกอบด้วยการตั้งค่าข้อกำหนดเบื้องต้นทั้งหมด จากนั้น เราจะสร้างสัญญา Ethereum ฉบับเดียวและทดสอบกับ Truffle สุดท้าย เราจะแยก oracle ออกจากไคลเอนต์และทดสอบร่วมกัน

ข้อกำหนดของซอฟต์แวร์

  • ระบบปฏิบัติการหลักใดๆ จะใช้งานได้ แม้ว่าการติดตั้งและการตั้งค่าบางอย่างจะแตกต่างกันไปในแต่ละระบบ ฉันได้ทำสิ่งนี้ทั้งหมดบน Ubuntu Linux (16.04) ฉันยังไม่มีปัญหาในการตั้งค่าสภาพแวดล้อมบน Windows ฉันไม่ได้ลองใช้ Mac แม้ว่าฉันรู้ว่าเป็นเรื่องปกติที่จะลองใช้ Mac เช่นกัน
  • ไม่จำเป็นต้องรันโหนด eth แบบเต็ม เราจะใช้ Truffle ซึ่งมาพร้อมกับ testnet ของตัวเอง ถ้าคุณรู้บ้างเกี่ยวกับสิ่งที่คุณทำ คุณสามารถใช้ testnet อื่นที่คุณเลือกได้ dev testnet ในพื้นที่ของ Truffle เป็นเพียงวิธีที่ง่ายที่สุดและเข้าถึงได้มากที่สุดสำหรับวัตถุประสงค์ของบทช่วยสอนนี้

ข้อกำหนดความรู้

  • ความรู้พื้นฐานเกี่ยวกับการทำงานของบล็อคเชน
  • ทำความเข้าใจว่าสัญญาอัจฉริยะบนบล็อคเชนคืออะไร
  • ประสบการณ์การใช้งาน Hello worldish ขั้นพื้นฐานเกี่ยวกับการพัฒนาสัญญาอัจฉริยะจะเป็นประโยชน์ แต่ไม่จำเป็นหากคุณฉลาดและมีความทะเยอทะยาน (และฉันรู้ว่าคุณเป็น!)

ชุดบทความนี้ สามารถ ใช้เป็นบทนำเบื้องต้นเกี่ยวกับสัญญาอัจฉริยะได้ แต่จะขยายไปสู่แนวคิดขั้นสูงขึ้นอย่างรวดเร็ว หากเป็นบทแนะนำ eth smart contract ครั้งแรกของคุณ ให้เตรียมพร้อมที่จะปีนขึ้นไปบนที่สูงอย่างรวดเร็ว หากคุณรู้สึกมั่นใจ ยอดเยี่ยม ถ้าไม่ อย่าลังเลที่จะรับการสอนแบบ "สวัสดีชาวโลก" ที่ง่ายกว่าหรือสองอย่างภายใต้เข็มขัดของคุณก่อน ตรวจสอบบทความ Ethereum และ Cryptozombies หนึ่งหรือก่อนหน้าสำหรับผู้เริ่มต้น

Caveat: พื้นที่สัญญาอัจฉริยะที่ใหม่มาก เปลี่ยนแปลงอย่างรวดเร็ว ฟีเจอร์ไวยากรณ์ Solidity ที่ใหม่ตอนที่เขียนบทความนี้อาจจะเลิกใช้หรือล้าสมัยเมื่อคุณอ่านข้อความนี้ เวอร์ชัน Geth อาจมีมาและไป Solidity มักจะเพิ่มฟีเจอร์ภาษาใหม่ๆ และเลิกใช้ฟีเจอร์เก่าอยู่เสมอ ฟีเจอร์ใหม่มากมายกำลังดำเนินการอยู่ ดังนั้น ให้เตรียมพร้อมหากจำเป็นต้องปรับข้อมูลในบทความนี้ให้เข้ากับภูมิทัศน์ใหม่แห่งอนาคต หากคุณจริงจังกับการเรียนรู้การพัฒนาสัญญาอัจฉริยะ ฉันก็เชื่อมั่นในตัวเธอ

คำอธิบายของ Example App

กรณีการใช้งาน: ผู้ใช้เดิมพันการแข่งขันชกมวย

  • ผู้ใช้สามารถดึงรายการการแข่งขันชกมวยที่เดิมพันได้
  • ผู้ใช้สามารถเลือกการแข่งขันและวางเดิมพันกับผู้ชนะ
  • ผู้ใช้สามารถเดิมพันจำนวนใด ๆ ที่สูงกว่าขั้นต่ำที่กำหนด
  • หากการเลือกของผู้ใช้แพ้ ผู้ใช้จะเสียเงินเดิมพันทั้งหมด
  • หากการเลือกของผู้ใช้ชนะ ผู้ใช้จะได้รับส่วนหนึ่งของเงินกองกลางตามขนาดของการเดิมพันของเขา/เธอ และจำนวนเงินเดิมพันทั้งหมดของผู้แพ้ในการแข่งขัน หลังจากที่เจ้าบ้าน (เจ้าของสัญญา) รับเงินรางวัลเป็นเปอร์เซ็นต์เล็กน้อย .

Ethereum Oracle คืออะไร?

สัญญาอัจฉริยะยังคงเป็นสิ่งใหม่ พวกเขายังไม่ได้เข้าสู่กระแสหลัก และหลายๆ แง่มุมของวิธีการทำงานที่ยังไม่ได้ถูกตอกย้ำและทำให้เป็นมาตรฐาน ฉันจะอธิบายสั้นๆ ถึงแรงผลักดันที่อยู่เบื้องหลังแนวคิดของ "คำพยากรณ์"—และอดทนไว้ เราจะเจาะลึกมากขึ้นในตอนหลัง

วิศวกรรมสัญญาบล็อกเชนไม่เหมือนกับการเขียนโปรแกรมแอปไคลเอนต์ - เซิร์ฟเวอร์ ข้อแตกต่างที่สำคัญประการหนึ่งคือข้อมูลที่สัญญาโต้ตอบต้องอยู่ในบล็อกเชนอยู่แล้ว ไม่มีการเรียก ออก จากบล็อคเชน ไม่เพียงแค่ภาษาไม่รองรับเท่านั้น แต่ยังไม่ได้รับการสนับสนุนโดยกระบวนทัศน์ของบล็อคเชนอีกด้วย สัญญาสามารถเดิมพันในรูปแบบของสกุลเงินที่ใช้ Ethereum เก็บไว้ในสัญญาและปล่อยไปยังที่อยู่กระเป๋าเงินที่ถูกต้องตามสูตรเมื่อมีการประกาศผู้ชนะการแข่งขัน แต่สัญญารู้ได้อย่างไรว่าผู้ชนะ? ไม่สามารถสืบค้น REST API หรืออะไรทำนองนั้นได้ ใช้ได้เฉพาะข้อมูลที่มีอยู่ในบล็อคเชนเท่านั้น! กรณีการใช้งานสมาร์ทคอนแทรคหลายๆ กรณีประสบปัญหาที่คล้ายกัน—มีข้อจำกัดอย่างจริงจัง เว้นแต่จะสามารถโต้ตอบกับโลกภายนอกบล็อคเชนได้

หากสัญญาสามารถโต้ตอบกับข้อมูลบนบล็อคเชนเท่านั้น วิธีแก้ปัญหาที่ชัดเจนคือการป้อนข้อมูลที่จำเป็นลงในบล็อคเชน และนั่นคือสิ่งที่เป็นคำพยากรณ์ Oracle เป็นอีกหนึ่งสัญญาที่แทรกข้อมูลลงในบล็อคเชน ทำให้สัญญาอื่นๆ ใช้งานได้ แม้ว่าสิ่งนี้จะทำให้เกิดคำถามเกี่ยวกับความไว้วางใจและความไม่ไว้วางใจ แต่ให้ยอมรับในตอนนี้ว่านั่นคือสิ่งที่ oracle เป็น ในส่วนที่ 3 ของชุดนี้ เราจะพูดถึงความแตกต่างเหล่านั้น ในกรณีการใช้งานตัวอย่างของเรา oracle จะเป็นสัญญาที่แทรกข้อมูลลงใน blockchain เกี่ยวกับ (a) การจับคู่ที่มีอยู่และ (b) ใครชนะการแข่งขันเหล่านั้น เมื่อตัดสินใจแล้ว

การตั้งค่าสภาพแวดล้อมการพัฒนา Ethereum

สำหรับการตั้งค่าพื้นฐาน เราจะติดตั้ง:

  • Geth (ตัวเลือกสำหรับตอนนี้)
  • แห้ว
  • Ganache CLI (ตัวเลือก)
  • สภาพแวดล้อมการพัฒนา (ไม่บังคับ)

บทความนี้ไม่มีที่ว่างสำหรับคู่มือการตั้งค่าสภาพแวดล้อมฉบับสมบูรณ์ แต่จะทำหน้าที่เป็นเพียงคำแนะนำคร่าวๆ ไม่เป็นไร เพราะมีคู่มือการตั้งค่าที่สมบูรณ์กว่ามากมายสำหรับระบบปฏิบัติการเฉพาะของคุณอยู่แล้ว และอินเทอร์เน็ตก็ไม่ต้องการระบบปฏิบัติการใหม่ ดังนั้นฉันจะนำคุณไปสู่เส้นทางอย่างรวดเร็วและชี้ให้คุณเห็นแหล่งข้อมูลเพื่อรับรายละเอียดเพิ่มเติมตามต้องการ เตรียมติดตั้งข้อกำหนดและข้อกำหนดเบื้องต้นตามที่ระบบของคุณต้องการและตามที่ Google แนะนำคุณ

ภาพประกอบกระบวนการสัญญาของ Oracle

ติดตั้ง Geth (ไม่บังคับ)

สกรีนช็อตของการติดตั้ง Geth คลิกเพื่อดูภาพขนาดเต็ม

Geth คือ Go-ethereum ซึ่งเป็นซอฟต์แวร์หลักของ Ethereum; แม้ว่าจะไม่จำเป็นสำหรับแบบฝึกหัดนี้เลย แต่ก็ถือว่านักพัฒนา Ethereum ทุกคนต้องมีและทำความคุ้นเคยกับมัน จำเป็นอย่างยิ่งหากคุณจะปรับใช้สัญญาอัจฉริยะกับเครือข่าย Ethereum แบบสด

  • http://www.talkcrypto.org/blog/2018/01/23/what-is-geth/
  • https://github.com/ethereum/go-ethereum/wiki/Installation-Instructions-for-Ubuntu
  • https://github.com/ethereum/go-ethereum/wiki/Installation-instructions-for-Windows

ติดตั้งทรัฟเฟิล

สกรีนช็อตของการติดตั้งทรัฟเฟิล คลิกเพื่อดูภาพขนาดเต็ม

ทรัฟเฟิลคือสิ่งสำคัญที่เราจะใช้ในการพัฒนาและเป็นข้อกำหนดสำหรับคู่มือนี้อย่างแน่นอน ค้นหาและปฏิบัติตามคำแนะนำเฉพาะสำหรับระบบปฏิบัติการของคุณเพื่อติดตั้ง Truffle ด้านล่างนี้คือลิงก์บางส่วนที่หวังว่าจะช่วยคุณได้

  • https://truffleframework.com/docs/truffle/getting-started/installation
  • https://github.com/trufflesuite/truffle
  • https://truffleframework.com/tutorials/how-to-install-truffle-and-testrpc-on-windows-for-blockchain-development

ติดตั้ง Ganache CLI (ไม่บังคับ)

สกรีนช็อตของการติดตั้ง Ganache CLI คลิกเพื่อดูภาพขนาดเต็ม

ฉันแนะนำให้ติดตั้ง Ganache CLI เพื่อใช้เป็นเครื่องมือทดสอบอื่น แม้ว่าเราจะไม่ใช้มันสำหรับบทช่วยสอนของเราจริงๆ เป็นทางเลือก

https://github.com/trufflesuite/ganache-cli

สภาพแวดล้อมการพัฒนา Ethereum

จะเป็นมากกว่าที่จะทำได้ในการทำบทช่วยสอนทั้งหมดนี้ด้วยโปรแกรมแก้ไขข้อความธรรมดาๆ เช่น Notepad++, gedit, vi หรือโปรแกรมแก้ไขข้อความหรือ IDE ที่คุณเลือก โดยส่วนตัวแล้วฉันกำลังใช้ Visual Studio Code กับส่วนขยายต่อไปนี้:

  • ความแข็งแกร่ง
  • เสริมความแข็งแกร่ง
  • ธีมไอคอนวัสดุ

หมายเหตุ: ไม่จำเป็นต้องใช้ส่วนขยาย—เพียงแค่สร้างสภาพแวดล้อมการเข้ารหัสที่ดีขึ้นเท่านั้น

การตั้งค่ารหัส

การติดตั้งโครงการ

ทรัฟเฟิลเป็นเครื่องมือที่สะดวกมากสำหรับการรวบรวมสัญญาอัจฉริยะ โยกย้ายไปยังบล็อกเชน และยังให้การพัฒนาและยูทิลิตี้การดีบัก จำเป็นต้องมีการตั้งค่าโครงการบางอย่างเพื่อรวมเข้ากับทรัฟเฟิล ตอนนี้ เราจะตั้งค่าเชลล์สำหรับโปรเจ็กต์ของเรา ทั้งในทรัฟเฟิลและในโครงสร้างไดเร็กทอรี เพียงแค่นั่งลง ทำตามขั้นตอนหุ่นยนต์ในตอนนี้ และสนุกได้เลย

สร้างไดเร็กทอรีเพื่อเก็บโค้ดทั้งหมด เรียกมันว่า oracle-example

ภายในไดเร็กทอรีราก ให้สร้างไดเร็กทอรีย่อยสองไดเร็กทอรี เพราะในที่สุด โปรเจ็กต์จะประกอบด้วยโปรเจ็กต์ย่อยสองโปรเจ็กต์ สร้างไดเร็กทอรี:

  • /oracle-example/client
  • /oracle-example/oracle

ไปที่โฟลเดอร์ไคลเอนต์ เพราะนั่นเป็นโครงการแรกที่เราจะพัฒนา เปิดหน้าต่างเทอร์มินัล (บรรทัดคำสั่ง) ในโฟลเดอร์ /oracle-example/client

เรียกใช้คำสั่ง truffle init

โปรดทราบว่าในหลายๆ ไฟล์ที่สร้างขึ้น ได้แก่ truffle-config.js และ truffle.js เราไม่ต้องการทั้งสองอย่าง ดังนั้น ให้ลบ truffle-config.js (เพื่อหลีกเลี่ยงความสับสนและความยุ่งเหยิง)

เราจำเป็นต้องแก้ไข truffle.js เพื่อชี้ Truffle ไปในทิศทางที่ถูกต้องสำหรับการทดสอบ แทนที่เนื้อหาของ truffle.js ด้วยสิ่งต่อไปนี้:

 module.exports = { networks: { development: { host: "localhost", port: 8545, network_id: "*" // Match any network id } } };

https://github.com/jrkosinski/oracle-example/tree/part1-step1/client/truffle.js

โปรดทราบว่า Truffle init สร้างไดเร็กทอรีที่เรียกว่าการ โยกย้าย ( oracle-example/client/migration ) ภายในโฟลเดอร์นั้นควรเป็นไฟล์ชื่อ 1_initial_migration.js

เพิ่มไฟล์อื่นในไดเร็กทอรี migrations และตั้งชื่อเป็น 2_deploy_contracts.js โดยมีเนื้อหาดังต่อไปนี้:

 var BoxingBets = artifacts.require("BoxingBets"); module.exports = function(deployer) { deployer.deploy(BoxingBets); };

https://github.com/jrkosinski/oracle-example/tree/part1-step1

การเพิ่มรหัส

ตอนนี้การตั้งค่าอย่างง่ายกำลังใกล้เข้ามาแล้ว เราก็พร้อมที่จะเริ่มเขียนโค้ดแล้ว โปรดจำไว้ว่า ส่วนนี้ของบทความยังคงเป็นการแนะนำและการตั้งค่า ดังนั้นเราจะดำเนินการอย่างรวดเร็วผ่านโค้ด เราจะพูดถึงคำอธิบายเชิงลึกของโค้ดในส่วนที่ 2 และการอภิปรายเชิงลึกเกี่ยวกับสถาปัตยกรรมและแนวคิดในส่วนที่ 3 ที่กล่าวว่า เราจะพูดถึงแนวคิดหลักบางอย่างที่เห็นได้ชัดในโค้ดอย่างรวดเร็ว ติดตามอย่างระมัดระวังเพื่อให้ทัน

รหัสเต็มสำหรับขั้นตอนนี้ในกระบวนการมีอยู่ใน GitHub: https://github.com/jrkosinski/oracle-example/tree/part1-step1

สัญญาในความเข้มแข็ง

“สัญญา” ใน Solidity นั้นคล้ายคลึงกับคลาสในภาษาเชิงวัตถุอื่นๆ ภาษานั้นถูกนำไปเปรียบเทียบกับ Golang และ JavaScript รวมถึงภาษาอื่นๆ โครงสร้างภาษาอื่นๆ ใน Solidity—ซึ่งเราจะมีตัวอย่างในภายหลัง—คือตัวดัดแปลง ไลบรารี และอินเทอร์เฟซ การสืบทอด (รวมถึงการสืบทอดหลายรายการ) ได้รับการสนับสนุนสำหรับสัญญา ไฟล์สัญญา Solidity มีนามสกุล .sol

Oracle Interface

เพิ่มไฟล์นี้ในโปรเจ็กต์ของคุณ: /oracle-example/client/contracts/OracleInterface.sol

https://github.com/jrkosinski/oracle-example/tree/part1-step1/client/contracts/OracleInterface.sol

สกรีนช็อตของอินเทอร์เฟซ oracle คลิกเพื่อดูภาพขนาดเต็ม

โดยปกติอินเทอร์เฟซของ oracle จะเป็นเพียงส่วนต่อประสาน สำหรับการทำซ้ำครั้งแรกนี้ เป็นเพียงคลาสธรรมดาที่อยู่ภายในโปรเจ็กต์ Solidity เช่นเดียวกับตัวยึดตำแหน่งสำหรับตอนนี้ เราจะย้ายออกไปในขั้นตอนต่อไป หลังจากที่เรารวบรวมและดำเนินการสัญญากับทรัฟเฟิลสำเร็จแล้ว หลังจากที่เราแปลงสิ่งนี้เป็นอินเทอร์เฟซจริงในภายหลัง การใช้งานฟังก์ชันจะว่างเปล่า

สัญญาลูกค้า

เพิ่มไฟล์นี้ในโครงการของคุณ: /oracle-example/client/contracts/BoxingBets.sol

https://github.com/jrkosinski/oracle-example/tree/part1-step1/client/contracts/BoxingBets.sol

นี่คือสัญญาที่ใช้ข้อมูลการแข่งขันชกมวย ให้ผู้ใช้สามารถค้นหาแมตช์ที่มีอยู่ และวางเดิมพันได้ ในการทำซ้ำในภายหลัง มันจะคำนวณและจ่ายเงินรางวัล

การรวบรวมและการทำงาน

ตอนนี้เป็นตอนที่เราจะดูว่าเราได้ทุกอย่างถูกต้องในครั้งแรก!

รวบรวมและย้ายสัญญา

เปิดเทอร์มินัลใน /oracle-example/client/ โฟลเดอร์

รวบรวมรหัสด้วยคำสั่งนี้:

 truffle compile 
สกรีนช็อตของการคอมไพล์และการย้ายข้อมูลสัญญา คลิกเพื่อดูภาพขนาดเต็ม
ภาพหน้าจอที่สองของการรวบรวมและการย้ายข้อมูลสัญญา คลิกเพื่อดูภาพขนาดเต็ม

ทางเลือกอื่น: ใช้สคริปต์เชลล์ recompile.sh ของฉัน (https://github.com/jrkosinski/oracle-example/tree/part1-step1/client/recompile.sh)

โปรดทราบว่าคุณจะเห็นคำเตือนมากมาย เนื่องจากโค้ดของเรายังไม่อยู่ในรูปแบบสุดท้าย!

เปิดคอนโซลการพัฒนาทรัฟเฟิล:

 truffle develop

ตอนนี้ในคอนโซลนักพัฒนาซอฟต์แวร์ Truffle ให้ย้ายไปที่เครือข่ายทดสอบ:

 truffle(develop)> migrate

ดำเนินสัญญา

ที่พรอมต์ของคอนโซลการพัฒนา ให้ป้อนบรรทัดของโค้ดต่อไปนี้:

 truffle(develop)> BoxingBets.deployed().then(inst => { instance = inst })

ตอนนี้ “ตัวอย่าง” เป็นตัวแปรที่อ้างถึงสัญญา BoxingBets และสามารถใช้เรียกวิธีการสาธารณะได้

ทดสอบโดยใช้คำสั่งต่อไปนี้:

 truffle(develop)> instance.test(3, 4)

โปรดทราบว่าเราได้รวมฟังก์ชัน "ทดสอบ" สาธารณะใน BoxingBets.sol มันรวมตัวเลขสองตัวใดก็ตามที่คุณส่งเข้าไป เพื่อแสดงให้เห็นว่าสัญญากำลังรันโค้ด และเราสามารถเรียกมันได้จากคอนโซลการพัฒนา Truffle หากเราได้รับการตอบสนองที่สมเหตุสมผล (ดูด้านล่าง) แสดงว่างานของเราเสร็จสิ้นแล้ว (อย่างน้อยก็ในตอนนี้)

แยก Ethereum Oracle

หากทุกอย่างสำเร็จลุล่วงไปหมดแล้ว สิ่งต่อไปที่เราจะทำคือแยกสัญญา oracle ออกจากสัญญา BoxingBets ในการใช้งานจริง สัญญาของ oracle จะแยกจากสัญญาของลูกค้าบนบล็อคเชน ดังนั้นเราจะต้องสามารถ:

ไดอะแกรมของกระบวนการสัญญา ethereum oracle

  • สร้างอินสแตนซ์ด้วยที่อยู่บล็อคเชน
  • เปลี่ยนที่อยู่ oracle แบบไดนามิกที่สัญญาของลูกค้าใช้เพื่ออ้างอิง oracle

กล่าวโดยสรุป สิ่งที่เราจะทำตอนนี้คือแยก oracle และลูกค้าออกเป็นสองหน่วยงานที่ทำสัญญา blockchain แยกกัน และทำให้พวกเขาคุยกัน ลูกค้าจะยกตัวอย่าง oracle ตามที่อยู่และเรียกมัน

สัญญาลูกค้า

อันดับแรก เราจะเปลี่ยนสัญญาลูกค้า (ไคลเอนต์) เพื่อให้อ้างอิงถึงอินเทอร์เฟซแบบไดนามิกไปยัง oracle แทนที่จะเป็นคลาสที่เป็นรูปธรรม จากนั้นเราจะตรวจสอบให้แน่ใจว่าได้ยกตัวอย่าง oracle จากสัญญาภายนอก

เข้าไป ที่ /oracle-example/client/contracts/OracleInterface.sol ดังที่เราได้กล่าวไว้ก่อนหน้านี้ว่านี่ไม่ใช่อินเทอร์เฟซ แต่เรากำลังจะทำให้เป็นอินเทอร์เฟซ แทนที่สิ่งที่อยู่ในนั้นด้วยเนื้อหาของ:

https://github.com/jrkosinski/oracle-example/tree/part1-step2/client/contracts/OracleInterface.sol

 pragma solidity ^0.4.17; contract OracleInterface { enum MatchOutcome { Pending, //match has not been fought to decision Underway, //match has started & is underway Draw, //anything other than a clear winner (eg cancelled) Decided //index of participant who is the winner } function getPendingMatches() public view returns (bytes32[]); function getAllMatches() public view returns (bytes32[]); function matchExists(bytes32 _matchId) public view returns (bool); function getMatch(bytes32 _matchId) public view returns ( bytes32 id, string name, string participants, uint8 participantCount, uint date, MatchOutcome outcome, int8 winner); function getMostRecentMatch(bool _pending) public view returns ( bytes32 id, string name, string participants, uint participantCount, uint date, MatchOutcome outcome, int8 winner); function testConnection() public pure returns (bool); function addTestData() public; }

ใน BoxingBets.sol เราจะแทนที่บรรทัดนี้:

 OracleInterface internal boxingOracle = new OracleInterface();

ด้วยสองบรรทัดนี้:

 address internal boxingOracleAddr = 0; OracleInterface internal boxingOracle = OracleInterface(boxingOracleAddr);

สิ่งที่เราต้องการคือวิธีตั้งค่าที่อยู่ของ oracle แบบไดนามิก และฟังก์ชันที่เราสามารถเรียกใช้เพื่อค้นหาที่อยู่ของ oracle ปัจจุบันได้ เพิ่มสองฟังก์ชันนี้ใน BoxingBets.sol :

 /// @notice sets the address of the boxing oracle contract to use /// @dev setting a wrong address may result in false return value, or error /// @param _oracleAddress the address of the boxing oracle /// @return true if connection to the new oracle address was successful function setOracleAddress(address _oracleAddress) external onlyOwner returns (bool) { boxingOracleAddr = _oracleAddress; boxingOracle = OracleInterface(boxingOracleAddr); return boxingOracle.testConnection(); } /// @notice gets the address of the boxing oracle being used /// @return the address of the currently set oracle function getOracleAddress() external view returns (address) { return boxingOracleAddr; }

และสุดท้าย สำหรับการทดสอบการเชื่อมต่อระหว่างไคลเอนต์และ oracle เราสามารถแทนที่ฟังก์ชัน การทดสอบ ใน BoxingBets ด้วยฟังก์ชันเพื่อทดสอบการเชื่อมต่อ oracle:

 /// @notice for testing; tests that the boxing oracle is callable /// @return true if connection successful function testOracleConnection() public view returns (bool) { return boxingOracle.testConnection(); }

เป็นเจ้าของได้

ขอให้สังเกตว่าคำจำกัดความสำหรับ setOracleAddress มีตัวแก้ไข onlyOwner ตามหลัง ที่จำกัดฟังก์ชันนี้ไม่ให้ถูกเรียกโดยบุคคลอื่นที่ไม่ใช่เจ้าของสัญญา แม้ว่าฟังก์ชันจะเป็นแบบสาธารณะ นั่นไม่ใช่คุณสมบัติทางภาษา สัญญาที่เป็นเจ้าของได้จัดเตรียมไว้ให้เรา ซึ่งถูกยกออกจากไลบรารีของสัญญา Solidity ยูทิลิตี้ทั่วไปของ OpenZeppelin เราจะเข้าไปดูรายละเอียดในส่วนที่ 2 แต่เพื่ออำนวยความสะดวกในการใช้งานตัวปรับแต่ง onlyOwner เท่านั้น เราจำเป็นต้องทำการเปลี่ยนแปลงเล็กน้อย:

คัดลอก Ownable.sol จาก https://github.com/jrkosinski/oracle-example/tree/part1-step2/client/contracts/Ownable.sol ลงใน /oracle-example/client/contracts/

เพิ่มการอ้างอิงไปที่ด้านบนของ BoxingBets.sol เช่น:

 import "./Ownable.sol";

(คุณสามารถเพิ่มได้เพียงภายใต้บรรทัดที่นำเข้า OracleInterface.sol )

แก้ไขการประกาศสัญญาของ BoxingBets เพื่อให้สืบทอดจาก Ownable จากสิ่งนี้:

 contract BoxingBets {

สำหรับสิ่งนี้:

 contract BoxingBets is Ownable {

และเราควรจะพร้อมทั้งหมด รหัสเต็มอยู่ที่นี่ในกรณีที่คุณหลงทาง: https://github.com/jrkosinski/oracle-example/tree/part1-step2/client/contracts

Oracle Contracts

ติดตั้ง

ตอนนี้สัญญา BoxingBets กำลังพยายามอ้างถึงสัญญาที่แยกจากกันโดยสิ้นเชิง (นั่นคือ oracle) ตามที่อยู่ งานต่อไปของเราคือการสร้างสัญญา oracle นั้น ตอนนี้เราจะสร้างโปรเจ็กต์แยกกันทั้งหมดที่จะมีสัญญาออราเคิล โดยพื้นฐานแล้วมันเป็นการตั้งค่าเดียวกันกับที่เราได้ทำไปแล้วสำหรับโครงการสัญญาของลูกค้า กล่าวคือ ตั้งทรัฟเฟิลเพื่อรวบรวมและพัฒนา

คุณควรมีโฟลเดอร์ชื่อ /oracle-example/oracle/ ซึ่งเราสร้างไว้ในขั้นตอนก่อนหน้าแล้ว (หรือหากไม่มี ให้สร้างไดเร็กทอรีว่างนั้นทันที) เปิดเทอร์มินัลในไดเร็กทอรีนั้น

  • เรียกใช้คำสั่ง truffle init
  • ลบ /oracle-example/oracle/truffle-config.js
  • แก้ไข /oracle-example/oracle/truffle.js ดังนี้:
 module.exports = { networks: { development: { host: "localhost", port: 8545, network_id: "*" // Match any network id } } };

ดูตัวอย่างที่นี่: https://github.com/jrkosinski/oracle-example/tree/part1-step2/oracle/truffle.js

ภายใน /oracle-example/oracle/migrations/ ให้สร้างไฟล์ชื่อ 2_deploy_contracts.js โดยมีเนื้อหาดังต่อไปนี้:

 var BoxingOracle = artifacts.require("BoxingOracle"); module.exports = function(deployer) { deployer.deploy(BoxingOracle); };

ดูตัวอย่างที่นี่: https://github.com/jrkosinski/oracle-example/tree/part1-step2/oracle/migration/2_deploy_contracts.js

Oracle Code

สำหรับขั้นตอนนี้ เพียงคัดลอกไฟล์สามไฟล์ต่อไปนี้จาก https://github.com/jrkosinski/oracle-example/tree/part1-step2/oracle/contracts/ ลงในโฟลเดอร์ /oracle-example/oracle/contracts/ ของคุณ:

  • BoxingOracle.sol: สัญญาหลักของ oracle
  • Ownable.sol: สำหรับฟังก์ชั่นสำหรับเจ้าของเท่านั้น ตามที่เราใช้ในสัญญาของลูกค้าแล้ว
  • DateLib.sol: ไลบรารีวันที่ เราจะเจาะลึกลงไปในส่วนที่ 2 ของซีรีส์นี้

การทดสอบ Oracle

ในการทำซ้ำปัจจุบันของโปรเจ็กต์ เราจำเป็นต้องทดสอบ smart contract oracle อย่างละเอียดถี่ถ้วน เนื่องจากนั่นจะเป็นฐานของเราที่เราจะสร้างส่วนที่เหลือของโปรเจ็กต์ ตอนนี้เราได้ตั้งค่าโปรเจ็กต์ oracle และคัดลอกโค้ดแล้ว เราจะต้อง:

  • รวบรวมออราเคิล
  • ตรวจสอบให้แน่ใจว่า oracle ทำงาน
  • เรียกใช้ฟังก์ชันบางอย่างในคอนโซล Truffle เพื่อให้แน่ใจว่า oracle ทำงานตามที่คาดไว้

รวบรวมและโอนย้าย Oracle

ยังอยู่ในเทอร์มินัลที่เปิด /oracle-example/oracle/ ให้รันคำสั่งต่อไปนี้ อีกครั้ง ขั้นตอนเหล่านี้เหมือนกับที่เราได้ทำไปแล้วเพื่อรวบรวมและย้ายสัญญาของลูกค้า

 truffle compile

ทางเลือกอื่น: ใช้สคริปต์เชลล์ recompile.sh ของฉัน (https://github.com/jrkosinski/oracle-example/tree/part1-step2/oracle/recompile.sh)

เปิดคอนโซลการพัฒนาทรัฟเฟิล:

 truffle develop

ย้ายไปยังเครือข่ายทดสอบ:

 truffle(develop)> migrate

เรียกใช้และทดสอบ Oracle

ยังอยู่ในคอนโซลการพัฒนา Truffle ให้ป้อนข้อมูลนี้เพื่อจับตัวชี้ที่ใช้งานได้ไปยังสัญญา Oracle:

 truffle(develop)> BoxingOracle.deployed().then(inst => { instance = inst })

ตอนนี้ เราสามารถ (และควร) เรียกใช้ชุดการทดสอบในสัญญา oracle ของเราเพื่อทดสอบ ลองรันคำสั่งต่อไปนี้ทีละคำสั่ง และตรวจสอบผลลัพธ์

 truffle(develop)> instance.testConnection() ... truffle(develop)> instance.getAllMatches() ... truffle(develop)> instance.addTestData() ... truffle(develop)> instance.getAllMatches() ...

ถึงจุดนี้ ขอแนะนำให้คุณดูรหัส oracle ดูว่ามีวิธีสาธารณะใดบ้าง อ่านความคิดเห็นในโค้ด และทำการทดสอบของคุณเองเพื่อเรียกใช้ (และเรียกใช้ที่นี่ในคอนโซล เช่น ที่แสดงไว้ด้านบน)

การทดสอบและการดีบัก

ตอนนี้เราพร้อมสำหรับการทดสอบขั้นสุดท้ายแล้ว: เพื่อทดสอบว่าสัญญาของลูกค้าสามารถเรียกสัญญา oracle ที่อยู่บน blockchain อยู่แล้ว และดึงเข้ามาและใช้ข้อมูลของมัน หากทั้งหมดนี้ใช้ได้ผล เราก็มีคู่ไคลเอนต์กับออราเคิลที่เราสามารถใช้สำหรับการทดลองต่อไปได้ ขั้นตอนของเราในการรันการทดสอบแบบ end-to-end:

  • คอมไพล์และรัน oracle contract
  • รวบรวมและเรียกใช้สัญญาลูกค้า
  • รับที่อยู่ของสัญญา oracle
  • ตั้งค่าที่อยู่ oracle ในสัญญาลูกค้า
  • เพิ่มข้อมูลทดสอบลงในสัญญาของ Oracle
  • ทดสอบว่าเราสามารถดึงข้อมูลนั้นในสัญญาลูกค้าได้

เปิดหน้าต่างเทอร์มินัลสองหน้าต่าง:

  • หนึ่งใน /oracle-example/client/
  • และอีกอันใน /oracle-example/oracle/

ฉันแนะนำให้คุณเปิด /oracle-example/client/ ไว้ทางด้านซ้ายและ /oracle-example/oracle/ อันหนึ่งเปิดทางด้านขวา และปฏิบัติตามอย่างใกล้ชิดเพื่อหลีกเลี่ยงความสับสน

คอมไพล์และรัน Oracle Contract

ดำเนินการคำสั่งต่อไปนี้ในเทอร์มินัล /oracle-example/oracle/ :

 bash recompile.sh truffle develop truffle(develop)> migrate truffle(develop)> BoxingOracle.deployed().then(inst => { instance = inst })

รวบรวมและเรียกใช้สัญญาลูกค้า

ดำเนินการคำสั่งต่อไปนี้ในเทอร์มินัล /oracle-example/client/ :

 bash recompile.sh truffle develop truffle(develop)> migrate truffle(develop)> BoxingBets.deployed().then(inst => { instance = inst })

รับที่อยู่ของสัญญา Oracle

ดำเนินการคำสั่งต่อไปนี้เพื่อ Truffle ใน /oracle-example/oracle/ เทอร์มินัล:

 truffle(develop)> instance.getAddress()

คัดลอกที่อยู่ซึ่งเป็นผลลัพธ์จากการโทรนี้ และนำไปใช้ในขั้นตอนต่อไป

ตั้งค่าที่อยู่ Oracle ในสัญญาลูกค้า

ดำเนินการคำสั่งต่อไปนี้เพื่อทรัฟเฟิลใน /oracle-example/client/ เทอร์มินัล:

 truffle(develop)> instance.setOracleAddress('<insert address here, single quotes included>')

และทดสอบ:

 truffle(develop)> instance.testOracleConnection()

ถ้าผลลัพธ์เป็น true เราก็พร้อม

ทดสอบว่าเราสามารถดึงข้อมูลนั้นในสัญญาลูกค้าได้

ดำเนินการคำสั่งต่อไปนี้เพื่อทรัฟเฟิลใน /oracle-example/client/ เทอร์มินัล:

 truffle(develop)> instance.getBettableMatches()

ควรส่งคืนอาร์เรย์ว่าง เนื่องจากยังไม่มีการเพิ่มข้อมูลทดสอบในฝั่ง oracle

ดำเนินการคำสั่งต่อไปนี้เพื่อทรัฟเฟิลใน /oracle-example/oracle/ เทอร์มินัลเพื่อเพิ่มข้อมูลทดสอบ:

 truffle(develop)> instance.addTestData()

ดำเนินการคำสั่งต่อไปนี้เพื่อทรัฟเฟิลใน /oracle-example/client/ เทอร์มินัล เพื่อดูว่าเราสามารถรับข้อมูลทดสอบที่เพิ่มใหม่จากลูกค้าได้หรือไม่:

 truffle(develop)> instance.getBettableMatches()

ตอนนี้ หากคุณนำที่อยู่แต่ละรายการจากอาร์เรย์ที่ส่งคืนโดย getBettableMatches() กลับมา และเสียบเข้ากับ getMatch()

ถึงจุดนี้ ขอแนะนำให้คุณดูโค้ดไคลเอ็นต์ ดูว่ามีวิธีสาธารณะใดบ้าง อ่านความคิดเห็นในโค้ด และทำการทดสอบของคุณเองเพื่อเรียกใช้ (และเรียกใช้ที่นี่ในคอนโซล เช่น ข้างต้น).

บทสรุปของภาคที่หนึ่ง

ผลลัพธ์ของเราจากแบบฝึกหัดนี้มีจำกัด แต่เป้าหมายของเราก็เช่นกัน เพื่อที่จะรักษาอัตราการก้าวตามความเป็นจริง ลูกค้าของเรายังไม่มีความสามารถในการเดิมพัน จัดการเงิน แบ่งเงินรางวัล ฯลฯ สิ่งที่เรามี—นอกเหนือจากความรู้และประสบการณ์ที่ได้รับ—คือ:

  • oracle สัญญาอัจฉริยะที่ใช้งานได้ส่วนใหญ่
  • ลูกค้าที่สามารถเชื่อมต่อและโต้ตอบกับ oracle
  • กรอบการทำงานเพื่อการพัฒนาและการเรียนรู้ต่อไป

และนั่นก็ไม่เลวสำหรับบทความสั้น ๆ

ใน ส่วนที่สองของซีรีส์นี้ เราจะเจาะลึกลงไปในโค้ดและดูคุณลักษณะบางอย่างที่เป็นเอกลักษณ์เฉพาะของการพัฒนาสัญญาอัจฉริยะ ตลอดจนคุณลักษณะทางภาษาบางอย่างเฉพาะสำหรับ Solidity หลายสิ่งหลายอย่างที่เพิ่งจะกลบเกลื่อนในส่วนนี้จะอธิบายในตอนต่อไป

ใน ส่วนที่สามของซีรีส์นี้ เราจะพูดคุยกันเล็กน้อยเกี่ยวกับปรัชญาและการออกแบบสัญญาอัจฉริยะ โดยเฉพาะที่เกี่ยวข้องกับการใช้งานกับ oracles

ขั้นตอนเพิ่มเติมเพิ่มเติม

การทดลองคนเดียวเป็นวิธีที่ดีในการเรียนรู้ ต่อไปนี้คือคำแนะนำง่ายๆ สองสามข้อหากคุณกำลังคิดหาวิธีขยายบทช่วยสอนนี้เพื่อความรู้ที่มากขึ้น (ส่วนใดต่อไปนี้จะไม่ครอบคลุมในส่วนที่ 2 และ 3)

  • ปรับใช้สัญญากับ Ganache (เดิมคือ testrpc) และเรียกใช้การทดสอบเดียวกันเพื่อตรวจสอบฟังก์ชัน
  • ปรับใช้สัญญาเพื่อทดสอบ ropsten หรือ rinkeby และเรียกใช้การทดสอบเดียวกันเพื่อตรวจสอบการทำงาน
  • สร้างส่วนหน้าของ web3js สำหรับ oracle หรือไคลเอนต์ (หรือทั้งสองอย่าง)

ขอให้โชคดีและโปรดติดต่อฉันหากมีคำถามใด ๆ ฉันไม่สามารถรับประกันได้ว่าจะต้องตอบกลับอย่างรวดเร็ว แต่ฉันจะทำให้ดีที่สุด