สัญญา Ethereum Oracle: การตั้งค่าและการวางแนว
เผยแพร่แล้ว: 2022-03-11Ethereum 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 แนะนำคุณ
ติดตั้ง 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 เพื่อใช้เป็นเครื่องมือทดสอบอื่น แม้ว่าเราจะไม่ใช้มันสำหรับบทช่วยสอนของเราจริงๆ เป็นทางเลือก
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 จะเป็นเพียงส่วนต่อประสาน สำหรับการทำซ้ำครั้งแรกนี้ เป็นเพียงคลาสธรรมดาที่อยู่ภายในโปรเจ็กต์ 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 จะแยกจากสัญญาของลูกค้าบนบล็อคเชน ดังนั้นเราจะต้องสามารถ:
- สร้างอินสแตนซ์ด้วยที่อยู่บล็อคเชน
- เปลี่ยนที่อยู่ 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 หรือไคลเอนต์ (หรือทั้งสองอย่าง)
ขอให้โชคดีและโปรดติดต่อฉันหากมีคำถามใด ๆ ฉันไม่สามารถรับประกันได้ว่าจะต้องตอบกลับอย่างรวดเร็ว แต่ฉันจะทำให้ดีที่สุด