Facebook Libra focus on Consensus (Part 2)

Apisak Srihamat
4 min readAug 13, 2019

--

วันนี้เราจะมาศึกษาเรื่อง libra consensus กันต่อไปนะครับ อ้างอิงความเดิมจากตอนที่แล้วเรื่อง Facebook libra focus on Consensus (Part 1) และ Facebook Libra focus on create testnet-like ซึ่งเราได้รัน validators 4 node พร้อมกันเพื่อที่จะสามารถทำความเข้าใจขั้นตอนของการ consensus ได้อย่างถูกต้องต่อไป อย่างไรก็ตามเรามาสรุปความเข้าใจของ validators ทั้ง 4 node ที่เราสร้างกันอีกสักนิดนึงนะครับจะได้เห็นภาพเดียวกันชัดๆ

ใช้คำสั่ง docker ps เพื่อดูภาพรวมว่าเรามี docker run อยู่กี่ container แต่ละตัวมีรายละเอียดตามรูปนะครับ (ระหว่างที่ผมกำลังทำการทดสอบมีการเปลี่ยน port ไปเป็นดังรูปด้านล่างนะครับ)
ซึ่งเราสามารถนำข้อมูล docker ดังกล่าวมาวาดรูป overview system ได้ดังนี้ครับ

จากรูป overview system จะเห็นว่าเรามี validator ทั้งหมด 4 nodes ซึ่งทั้ง 4 nodes มีการเชื่อมต่อกันผ่าน port 6180 (validator network) นอกจากนี้เรายังมีการรัน client ที่ bash (ไม่ได้อยู่ใน docker) เพื่อ connect Node ไปที่ admission control ผ่าน port 8000 (IP address 172.18.0.13) แล้ว port 9101 (metrics) ล่ะเอาไว้ทำอะไร ? ดูจาก source code service_metrics.rs แล้วมีจำนวน request และจำนวน error ของแต่ละ gRPC server ดูแล้วน่าจะเป็นการวางแผนสำหรับ monitoring หรือ dashboard ที่จะดู healthy ของ node นั่นเองครับ

พบว่าในไฟล์ config/src/config.rs มีการ config metrics_server_port 9101 จัดให้อยู่ในกลุ่มเดียวกับ debug interface อื่นๆ ก็น่าจะหมายความได้ว่าเอาไว้สำหรับกระทำการ debug นั่นเอง ว่าแต่จะมีการใช้งาน port นี้ยังไงกันนะ ? ^ ^”

ในระหว่างเขียนบทความนี้ผมได้ทำการตรวจสอบ source code การใช้งาน port 9101 จึงพบว่ามีการสร้าง bench docker ที่จะทำการเชื่อมต่อไปที่ metrics_server_port เมื่อเร็วๆนี้ แต่มีการอ้างอิงถึง port 14297 (port เก่าที่ใช้อ้างอิงถึง metrics_server_port) จึงไม่รอช้าสร้าง PR#523 เพื่อช่วยป้องกันข้อผิดพลาดระหว่างทีมงานพัฒนา จากนั้นจึงทำการ build docker ทั้งหมดใหม่อีกครั้งเพื่อทดสอบ แต่ว่ายังไม่มีคำสั่ง run.sh เพื่อ run bench docker มาให้แฮะ… สงสัยว่าทีมงานพัฒนา libra จะยังทำงานส่วนนี้ไม่เสร็จ เพื่อไม่ให้เป็นการเสียเวลาเราค้างไว้ตรงนี้ก่อน แล้วปล่อยให้เขาทำงานของเขาไปก่อนละกันนะครับ 55+

ระหว่างที่ไล่มอง configuration port ผมก็ไปเจอ port ที่น่าสนใจ port 6185 ที่มี service ชื่อว่า “secret service “ ชื่อมันชั่งน่าค้นหาจริงๆ :)

อย่างไรก็ตาม เรากลับมาที่การทำความเข้าใจการ consensus กันต่อนะครับ โดยเริ่มด้วยการอ่าน log ที่ออกมาจาก validator (node IP 172.18.0.13) เพื่อเข้าใจภาพรวมของการทำงานกันก่อน

จุดเริ่มเป็น constructor ของ object VM executor ที่เริ่มสร้าง object ที่เกี่ยวข้องต่างๆ
จากนั้นจึงทำการเรียก start object PersistentStorage เนื่องจากยังไม่มีข้อมูลจึงพยายามจะเริ่ม consensus recovery ก่อน แล้วจากนั้น local_pacemaker พบว่า Round 1 timeout จึงทำการ broadcasting new round message to all replicas
กระทำการส่ง timeout message ออกไปผ่าน channel external_timeout_sender เพื่อบอก node อื่นๆ ซึ่งเจ้า LocalPacemakerInner จะคอยช่วยนับเวลา timeout ที่่กว้างพอเพื่อ guarantee การเหลื่อมกันของ “current round” ในหลายๆ node ที่กำลังทำงานร่วมกันอยู่

ตาม protocol แล้ว LocalPacemakerInner จะจัดการ ‘highest_certified_round’ ที่คอยเก็บข้อมูลจำนวนสูงสุดของ certified block ให้กับ validator ในแต่ละรอบการทำงาน (round) เมื่อมี QC (quorum certificate)ใหม่ที่มี round มากกว่าข้อมูล round ใน ‘highest_certified_round’ เจ้า local pacemaker จะเพิ่มค่า round และใช้ default timeout. *โดยปรกติแล้วทุกๆช่วงเวลา timeout local pacemaker จะเพิ่มค่า round และ doubles timeout.

เมื่อติดตาม source code ต่อไปจะเห็นว่า function process_outgoing_pacemaker_timeout ถูกเรียกเพราะว่ามีความต้องการส่ง message timeout เพื่อบอก replica หรือ node อื่นๆนั่นเอง ซึ่งในขั้นตอนนี้ replica จะหยุดการ vote สำหรับ round นี้และทำการ saves consensus state ของมันเองไว้ เหตุที่ต้องหยุดการ vote เพื่อให้มั่นใจว่า next proposer สามารถที่จะสร้าง proposal ที่สามารถจะทำการ vote ได้โดยทุกๆ replicas. ส่วนการ saving consensus state นั้นเพื่อให้มั่นใจว่าหาก restart, ทุกๆ replicas จะไม่ต้องเสียเวลาจัดการกับ rounds ก่อนหน้า

จุดที่น่าสนใจอีกจุดคือแล้วมันมีข้อความบอกว่า broadcasting new round to all replicas ว่าแต่มัน broadcasting อย่างไงกันนะ ?

การจัดเตรียมข้อมูล object TimeoutMsg เพื่อเตรียมพร้อมที่จะส่งข้อมูล broadcasting ซึ่งอยู่ใน function process_outgoing_pacemaker_timeout
และแล้วเราก็พบว่า broadcast จะถูกทำโดย function broadcast_timeout_msg ของ object ConsensusNetworkImpl ที่จะถูกเรียกใน async function process_outgoing_pacemaker_timeouts ที่ถูก spawn process ขึ้นมาเพื่อ loop รอรับ event timeout โดย EventProcessor นั่นเองครับ
function broadcast_timeout_msg ของ object ConsensusNetworkImpl
function broadcast ของ object ConsensusNetworkImpl ซึ่งอธิบายการทำงานได้ตรงไปตรงมาโดยการใช้ for loop แต่ละ peer ภายใน peers list
function send_to ของ object ConsensusNetworkSender จะเห็นว่ามีความพยายามที่จะใช้การ wrap NetworkRequest ในหลายๆรูปแบบ
Network API implemented NetworkRequest มี 3 แบบ คือ RPC, Message และ UpdateEligibleNodes (อันนี้ก็น่าสนใจนะเนี่ย ใช้เงื่อนไขอะไรตัดสินว่าเป็น eligible node กันนะ :)

Network component นั้นถูกออกแบบมาเพื่ออำนวยความสะดวกให้กับการทำ consensus และ shared mempool protocol โดยเฉพาะ ซึ่งมีการทำงานหลักๆ 2 อย่างคือ

  1. RPC: Remote Procedure Calls
  2. DirectSend: สำหรับการส่ง message ไปที่ผู้รับคนใดคนหนึ่ง ซึ่งเป็นการส่งโดยไม่จดจำ (fire-and-forget style)

ซึ่ง Network component ก็ถูกพัฒนาเป็น Actor model มี message คุยกันระหว่าง sub-components ด้วย tokio framwork อีกเช่นเคย ซึ่ง actor model มี sub-component ย่อยๆ ดังนี้

  1. Network provider: เป็น network API สำหรับ clients. มันจะคอย forwards requests จาก upstream clients ไปที่ downstream components และส่ง incoming RPC และ DirectSend requests ไปที่ upstream handler ที่เหมาะสม
  2. Peer Manager: คอยฟัง incoming connections และ dials จาก peers อื่นๆบน network. และมันจะช่วยแจ้งให้ components อื่นๆทราบเกี่ยวกับการ new/lost connections events และ demultiplexes incoming sub streams ไปให้ protocol handlers ที่เหมาะสมต่อไป
  3. Connectivity Manager: ทำให้มั่นใจว่าเราจะยังคงเชื่อมต่อกับแต่ละ node เฉพาะหากมันเป็น eligible member ของ network. ซึ่งมันจะได้รับ addresses ของ peers จาก discovery component และสั่ง dial/disconnect requests ไปที่ Peer Manager.
  4. Discovery: ใช้ push-style gossip สำหรับค้นพบ new peers และ updates addresses ของ existing peers. ทุกๆการ tick, มันจะทำการเปิด sub stream ใหม่ไปหา peer ที่ถูกสุ่มขึ้นมาและส่งสิ่งที่มันรับรู้ไปให้ peer. มันจะแจ้ง connectivity manager ให้ทราบหาก network มีการเปลี่ยนแปลงใดๆจาก inbound discovery messages.
  5. Health checker: จะทำการตรวจสอบ liveness เป็นช่วงเวลา (static timeout) เพื่อให้มั่นใจว่า peer/connections จะยังคงทำงานได้ดี
  6. Direct send: เปิดให้ ส่งหรือรับ messages ไปที่หรือมาถึง remote peers. ซึ่งมันจะแจ้ง upstream handler เมื่อมี inbound message.
  7. RPC: เปิดให้ ส่งหรือรับ RPC ไปที่หรือมาถึง peers อื่นๆ ซึ่งมันจะแจ้ง upstream handler เมื่อมี inbound RPC.

เอาล่ะครับหลังจากดูรายละเอียด Network component ที่คอยสนับสนุนขั้นตอนการทำงานของ Consensus กันจนพอเข้าใจบ้างแล้ว วันนี้ก็หมดเวลาอีกเช่นเคย หวังว่าจะเป็นประโยชน์ไม่มากก็น้อยครับ :)

บทความอื่นๆเกี่ยวกับการทำความเข้าใจ source code libra มีดังนี้ครับ

Facebook Libra on Windows is easy

Facebook Libra focus on account create

Facebook Libra focus on account mint

Facebook Libra focus on query balance

Facebook Libra focus on admission control (Part 1)

Facebook Libra focus on admission control (Part 2)

Facebook Libra focus on mempool

Facebook Libra focus on consensus (Part 1)

Facebook Libra focus on create testnet-like

--

--

Apisak Srihamat

Master of Science AIT, Embedded systems course UC Irvine, Bachelor of Computer Engineering KMITL, Love innovation ideas.