การพัฒนา Blockchain สำหรับอุตสาหกรรมโดยใช้ Go ส่วนที่ 1

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

การพัฒนา Blockchain สำหรับอุตสาหกรรมโดยใช้ Go ส่วนที่ 1

นี่เป็นบทความแรกในชุดบทความ ที่นี่ฉันอธิบายเซิร์ฟเวอร์และโปรโตคอล ในความเป็นจริง ผู้อ่านสามารถเขียนองค์ประกอบบล็อกเชนในเวอร์ชันของเขาเองได้

และนี่คือส่วนที่สอง — เกี่ยวกับบล็อกเชนและโครงสร้างข้อมูลธุรกรรม รวมถึงเกี่ยวกับแพ็คเกจที่ใช้การโต้ตอบกับฐานข้อมูล

เมื่อปีที่แล้วในงาน Digital Breakthrough hackathon พวกเขาเกิดแนวคิดที่จะสร้างระบบที่เป็นประโยชน์สำหรับอุตสาหกรรมและเศรษฐกิจดิจิทัลโดยใช้เทคโนโลยีบัญชีแยกประเภทแบบกระจาย นอกจากนี้ ยังมีการออกทุนสนับสนุนเพื่อการพัฒนาโดย Innovation Assistance Fund (ฉันควรเขียนรายงานแยกต่างหาก บทความเกี่ยวกับเงินช่วยเหลือสำหรับผู้ที่เพิ่งเริ่มต้นสตาร์ทอัพ ) และตอนนี้ตามลำดับ

การพัฒนาเกิดขึ้นในภาษา Go และฐานข้อมูลที่เก็บบล็อกคือ LevelDB
ส่วนหลักคือโปรโตคอล เซิร์ฟเวอร์ (ซึ่งรัน TCP และ WebSocket - ส่วนแรกสำหรับการซิงโครไนซ์บล็อคเชน ส่วนที่สองสำหรับการเชื่อมต่อไคลเอนต์ การส่งธุรกรรมและคำสั่งจาก JavaScript เป็นต้น

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

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

โฟลเดอร์ (ไดเร็กทอรีที่ Linux เรียก) ตามความเข้าใจของโปรแกรมเมอร์ Go เรียกว่าแพ็คเกจ ดังนั้นที่จุดเริ่มต้นของแต่ละไฟล์ที่มีโค้ด Go จากไดเร็กทอรีนี้ พวกเขาเขียนแพ็คเกจ folder_name_where_this_file อยู่ มิฉะนั้น คุณจะไม่สามารถฟีดแพ็คเกจไปยังคอมไพลเลอร์ได้ นี่ไม่ใช่ความลับสำหรับผู้ที่รู้ภาษานี้ เหล่านี้คือแพ็คเกจ:

  • การสื่อสารเครือข่าย (เซิร์ฟเวอร์ ไคลเอนต์ โปรโตคอล)
  • โครงสร้างของข้อมูลที่จัดเก็บและส่ง (บล็อก ธุรกรรม)
  • ฐานข้อมูล (บล็อกเชน)
  • ฉันทามติ
  • เครื่องเสมือนแบบเรียงซ้อน (xvm)
  • อุปกรณ์เสริม (crypto, ประเภท) นั่นคือทั้งหมดสำหรับตอนนี้

นี่คือลิงค์ไปยัง GitHub

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

มาดูที่เซิฟเวอร์กันก่อน

รูทีนย่อยของเซิร์ฟเวอร์ทำหน้าที่เป็นเซิร์ฟเวอร์ข้อมูลที่ทำงานบนโปรโตคอล TCP โดยใช้โครงสร้างข้อมูลจากแพ็คเกจโปรโตคอล

รูทีนใช้แพ็คเกจต่อไปนี้: เซิร์ฟเวอร์, โปรโตคอล, ชนิด. ในแพ็คเกจนั้นเอง tcp_server.go มีโครงสร้างข้อมูล บริการ.

type Serve struct {
	Port string
	BufSize int
	ST *types.Settings
}

สามารถยอมรับพารามิเตอร์ต่อไปนี้:

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

ความคืบหน้า:

  • อ่านการกำหนดค่าจากไฟล์ JSON
  • มีการตรวจสอบการตั้งค่าสถานะโหมดดีบัก: หากมีการตั้งค่าไว้ ตัวกำหนดเวลาการซิงโครไนซ์เครือข่ายจะไม่เปิดตัวและบล็อกเชนจะไม่ถูกโหลด
  • การเริ่มต้นโครงสร้างข้อมูลการกำหนดค่าและการเริ่มต้นเซิร์ฟเวอร์

เซิร์ฟเวอร์

  • ดำเนินการเปิดตัวเซิร์ฟเวอร์ TCP และการโต้ตอบเครือข่ายตามโปรโตคอล
  • มีโครงสร้างข้อมูลเสิร์ฟซึ่งประกอบด้วยหมายเลขพอร์ต ขนาดบัฟเฟอร์ และตัวชี้ไปยังโครงสร้าง ประเภทการตั้งค่า
  • วิธีการเรียกใช้เริ่มต้นการโต้ตอบของเครือข่าย (การฟังการเชื่อมต่อขาเข้าบนพอร์ตที่กำหนด เมื่อได้รับการเชื่อมต่อใหม่ การประมวลผลจะถูกถ่ายโอนไปยังวิธีจัดการส่วนตัวในเธรดใหม่)
  • В จัดการ ข้อมูลจากการเชื่อมต่อจะถูกอ่านลงในบัฟเฟอร์ แปลงเป็นตัวแทนสตริงและส่งผ่านไปยัง โปรโตคอลทางเลือก
  • โปรโตคอลทางเลือก ผลตอบแทน ผล หรือทำให้เกิดข้อผิดพลาด ผล แล้วโอนไปที่ โปรโตคอลตีความซึ่งกลับมา ภายใน - วัตถุประเภท ตีความข้อมูลหรือทำให้เกิดข้อผิดพลาดในการประมวลผลผลการคัดเลือก
  • จากนั้นสวิตช์จะถูกดำเนินการ intrpr.คำสั่ง[0] ซึ่งจะตรวจสอบหนึ่งใน: ผลลัพธ์ คำเชิญ ข้อผิดพลาด และมีส่วน ผิดนัด
  • ในส่วน ผล สวิตช์ถูกพบตามค่า intrpr.คำสั่ง[1] ซึ่งจะตรวจสอบค่าต่างๆ ความยาวบัฟเฟอร์ и รุ่น (ในแต่ละกรณีจะมีการเรียกใช้ฟังก์ชันที่เกี่ยวข้อง)

ฟังก์ชั่น รับเวอร์ชัน и บัฟเฟอร์Length อยู่ในไฟล์ srvlib.go แพ็คเกจเซิร์ฟเวอร์

GetVersion(conn net.Conn, version string)

เพียงพิมพ์ไปที่คอนโซลและส่งเวอร์ชันที่ส่งผ่านพารามิเตอร์ไปยังไคลเอนต์:

conn.Write([]byte("result:" + version))

.
ฟังก์ชัน

BufferLength(conn net.Conn, intrpr *protocol.InterpreteData)

โหลดบล็อก ธุรกรรม หรือข้อมูลเฉพาะอื่น ๆ ดังต่อไปนี้:

  • พิมพ์ประเภทข้อมูลที่ระบุในโปรโตคอลที่ต้องยอมรับไปยังคอนโซล:
    fmt.Println("DataType:", intrpr.Commands[2])
  • อ่านค่า intrpr.ร่างกาย ให้เป็นตัวแปรตัวเลข buf_len
  • สร้างบัฟเฟอร์ นิวบุฟ ขนาดที่ระบุ:
    make([]byte, buf_len)
  • ส่งการตอบกลับตกลง:
    conn.Write([]byte("result:ok"))
  • เติมบัฟเฟอร์จากสตรีมการอ่านโดยสมบูรณ์:
    io.ReadFull(conn, newbuf)

    .

  • พิมพ์เนื้อหาของบัฟเฟอร์ไปยังคอนโซล
    fmt.Println(string(newbuf))

    และจำนวนไบต์ที่อ่าน

    fmt.Println("Bytes length:", n)
  • ส่งการตอบกลับตกลง:
    conn.Write([]byte("result:ok"))

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

โปรโตคอล

โปรโตคอลทำหน้าที่เป็นวิธีการที่แสดงถึงข้อมูลในการแลกเปลี่ยนเครือข่าย

ตัวเลือก (สตริง str) (สตริง ข้อผิดพลาด) ดำเนินการประมวลผลข้อมูลเบื้องต้นที่เซิร์ฟเวอร์ได้รับ รับการแสดงสตริงของข้อมูลเป็นอินพุต และส่งคืนสตริงที่เตรียมไว้ ล่าม:

  • สตริงอินพุตจะถูกแบ่งออกเป็นส่วนหัวและลำตัวโดยใช้ ReqParseN2(str)
  • head ถูกแบ่งออกเป็นองค์ประกอบและวางไว้ในส่วนคำสั่งโดยใช้ ReqParseHead(head)
  • В สวิตช์ (คำสั่ง [0]) เลือกคำสั่งที่ได้รับ (cmd, คีย์, ที่อยู่ หรือส่วนถูกทริกเกอร์ ผิดนัด)
  • ตรวจสอบ 2 คำสั่งใน cmd สวิตช์ (คำสั่ง [1]) - ความยาว и การกลับใจใหม่.
  • ความยาว ตรวจสอบประเภทข้อมูลใน คำสั่ง[2] และบันทึกไว้ใน ประเภทข้อมูล
  • ตรวจสอบสิ่งนั้น ร่างกาย มีค่าสตริง
    len(body) < 1
  • ส่งกลับสตริงการตอบสนอง:
    "result:bufferlength:" + datatype + "/" + body
  • การกลับใจใหม่ ส่งคืนสตริง
    return "result:version/auto"

ล่าม

ประกอบด้วยโครงสร้าง InterpreteData และดำเนินการประมวลผลรองของข้อมูลที่ส่งคืน ทางเลือก สตริงและการก่อตัวของวัตถุ ตีความข้อมูล.

type InterpreteData struct {
	Head string
	Commands []string
	Body string
	IsErr bool
	ErrCode int 
	ErrMessage string
}

ฟังก์ชัน

Interprete(str string) (*InterpreteData, error)

ยอมรับสตริง ผล และสร้างและส่งกลับการอ้างอิงไปยังวัตถุ ตีความข้อมูล.

ความคืบหน้า:

  • เหมือนกับ ทางเลือก ศีรษะและลำตัวถูกสกัดโดยใช้ ReqParseN2(str)
  • หัวแบ่งออกเป็นองค์ประกอบโดยใช้ ReqParseHead(หัว)
  • วัตถุถูกเตรียมใช้งาน ตีความข้อมูล และตัวชี้จะถูกส่งกลับ:

res := &InterpreteData{
	Head: head,
	Commands: commands,
	Body: body,
}
return res, nil

วัตถุนี้ถูกนำมาใช้ใน เซิร์ฟเวอร์.ไป แพ็คเกจหลัก

ไคลเอนต์

แพ็คเกจไคลเอนต์ประกอบด้วยฟังก์ชันต่างๆ TCPConnect и TCPResponseData.

ฟังก์ชัน

TCPConnect(s *types.Settings, data []byte, payload []byte)

ทำงานดังนี้:

  • มีการเชื่อมต่อกับการเชื่อมต่อที่ระบุในออบเจ็กต์การตั้งค่าที่ส่งผ่าน
    net.Dial("tcp", s.Host + ":" + s.Port)
  • ข้อมูลที่ส่งผ่านพารามิเตอร์ข้อมูลจะถูกส่ง:
    conn.Write(data)
  • คำตอบคืออ่าน
    resp, n, _ := TCPResponseData(conn, s.BufSize)

    และพิมพ์บนคอนโซล

    fmt.Println(string(resp[:n]))
  • ถ้าโอนแล้ว น้ำหนักบรรทุก แล้วส่งต่อ
    conn.Write(payload)

    และยังอ่านการตอบสนองของเซิร์ฟเวอร์โดยพิมพ์ไปยังคอนโซล

ฟังก์ชัน

 TCPResponseData(conn net.Conn, bufsiz int) ([]byte, int, error)

สร้างบัฟเฟอร์ตามขนาดที่ระบุ อ่านการตอบสนองของเซิร์ฟเวอร์ที่นั่น และส่งคืนบัฟเฟอร์นี้และจำนวนไบต์ที่อ่าน รวมถึงอ็อบเจ็กต์ข้อผิดพลาด

รูทีนย่อยของลูกค้า

ทำหน้าที่ส่งคำสั่งไปยังเซิร์ฟเวอร์โหนด รวมถึงรับสถิติและการทดสอบโดยย่อ

สามารถยอมรับพารามิเตอร์ต่อไปนี้: ไฟล์การกำหนดค่าในรูปแบบ JSON, ข้อมูลที่จะส่งไปยังเซิร์ฟเวอร์ในรูปแบบสตริง, เส้นทางไปยังไฟล์ที่จะส่งไปยังเพย์โหลด, การตั้งค่าสถานะการจำลองตัวกำหนดเวลาโหนด, ประเภทของข้อมูลที่ถ่ายโอนเป็นค่าตัวเลข

  • รับการกำหนดค่า
    st := types.ParseConfig(*config)
  • หากผ่านธงนกอีมู มันจะเริ่มต้น ผู้จัดกำหนดการ
  • หากมีการระบุแฟล็ก f ที่ระบุเส้นทางไปยังไฟล์ เราจะโหลดข้อมูลเข้าไป FDB และเนื้อหาจะถูกส่งไปยังเซิร์ฟเวอร์
    client.TCPConnect(st, []byte(CMD_BUFFER_LENGTH + ":" + strconv.Itoa(*t) + "/" + strconv.Itoa(fdblen)), fdb)
  • หากไม่ได้ระบุไฟล์ ข้อมูลจากแฟล็กจะถูกส่งไป -d:
    client.TCPConnect(st, []byte(*data), nil)

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

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

ที่มา: will.com

เพิ่มความคิดเห็น