WLD очень популярен в эпоху переднего плана, и мем-монеты в это время также находятся в самом разгаре, поэтому я планирую посмотреть, что такое блокчейн.
Без лишних слов, Блокчейн на самом деле состоит из заголовка блока и блочного узла.
блочный узелсередина,Также включает предыдущий блокизадрес、Nonce、Временная метка、Блокировать информацию и т. д.
Давайте поговорим здесь об информации о блоках.
Целью каждого блокчейна является хранение информации о транзакциях, но блокчейн хранит только одну часть информации, особенно в памяти станции. Итак, есть ли способ хранить несколько транзакций? Массив вложенных связанных списков блокчейна? Теоретически это возможно, но возможно, что некий массив будет модифицирован и не сможет быть проверен.
Основная причина, по которой здесь упоминается проверка, заключается в том, что блокчейн имеет особенность: децентрализацию.
Что такое децентрализация?
Проще говоря,Традиционная архитектура B/S или C/S,К одному серверу или кластеру подключено множество клиентов.,А децентрализация означает,Сервер больше не управляет данными,Все данные существуют на каждом клиентском узле,наконец прошлоАлгоритм распределенного консенсусаДля достижения синхронизации блоков。
Но мы не будем здесь говорить о реализации децентрализованного распределенного консенсуса. Если вам интересно, вы можете взглянуть на алгоритм Raft и алгоритм bftp.
Для реализации кода здесь я использую Golang, поскольку Golang более удобен для последующего написания распределенного консенсуса, а его синтаксис проще, чем Java. Конечно, для его реализации можно также использовать C/C++.
type BlockChain struct {
Nodes []*BlockNode `json:"nodes,omitempty"` // массив узлов
Version string `json:"version,omitempty"` // номер версии
TreeSize int64 `json:"treeSize,omitempty"` // Дерево Меркельразмер
}
в этой структуре,записал сериюизмассив указателей блоков,текущий Блокчейнизномер версии,ForTreeSize,специфическийиз Он используется для выражения Блокчейнсередина,Дерево Меркельизмаксимумразмер(Для хранения больших объемов данных и облегчения проверки)。
type BlockNode struct {
// «Хеш-значение предыдущего узла»
Prev"Hash string `json:"prev"Hash,omitempty"`
// текущийузелизномер версии
Version string `json:"version,omitempty"`
// Временная метка создания текущего узла.
Datestamp long `json:"datestamp,omitempty"`
// Дерево в текущем узле Меркель
RootTree merkal.MerkalTree `json:"rootTree,omitempty"`
// «Хеш-код» Меркель
RootTreeCode string `json:"propertyCode,omitempty"`
// Жетон, используемый для расчета сложности, расчета nonce Процесс ценности заключается в блокировании header Непрерывно вычисляйте хэш, пока не найдете хеш блока, который меньше target из nonce。(здесь Не делай слишком многоиз Разрабатывать,Это связано с майнингом)
Nonce string `json:"nonce,omitempty"`
// Текущий узел Дерево Меркельизразмер
LocalTreeSize int64 `json:"LocalTreeSize,omitempty"`
}
Давайте сначала поговорим о Дереве Меркельизструктура данныхкаково этоиз,Почему это можно сделать быстро?Блокчейнизпроверять。
Предположим, у нас есть куча массивов сообщений, мы делим их на n групп, а затем группируем по парам и так далее, пока не останется один узел.
Содержимое, хранящееся в каждом узле, представляет собой «хеш-значение» двух узлов, из которых оно поступает.
type MerkalTree struct {
RootNode *MerkalNode `json:"rootNode,omitempty"`
DataSet *[]Message `json:"dataSet,omitempty"`
RootHash string `json:"root"Hash,omitempty"`
}
Если проверка не удалась, мы рекурсивно следуем за корневым узлом для проверки. Временная сложность равна $log_n$.
Что, если информация об узле представляет собой нечетное число?
Это очень просто. Просто сохраните его и объедините остальные. Объедините нечетные узлы в последнюю очередь или создайте копию нечетных узлов с самого начала. Однако это не рекомендуется, поскольку пространство обязательно увеличится вдвое.
package merkal
import (
"encoding/json"
"reflect"
"time"
)
type Message struct {
Title string `json:"title"`
Text string `json:"text"`
CreateTime time.Time `json:"createTime"`
}
func (this *Message) String() string {
res, _ := json.Marshal(this)
return string(res)
}
func (this *Message) Equals(other Message) bool {
return reflect.DeepEqual(this, other)
}
Хэш здесь нужно выносить отдельно, зачем? Поскольку это не адрес и не метод вычисления хэша, заключающийся в ручном прокручивании хеш-таблицы, как в языках на основе C, нам необходимо обеспечить строгую согласованность информации и узлов, поэтому мы хэшируем содержимое.
Так как же этого добиться?
Для реализации глубокого хэш-вычисления первым шагом является реализация метода глубокого копирования.
В Golang есть реализация глубокого копирования, но нам нужно ее зашифровать.
Мы можем имитировать способ, которым js реализует глубокое копирование, преобразовать его в строку json, а затем зашифровать строку json с помощью RSA, чтобы получить хэш-значение.
type BlockChain struct {
Nodes []*BlockNode `json:"nodes,omitempty"` // массив узлов
Version string `json:"version,omitempty"` // номер версии
TreeSize int64 `json:"treeSize,omitempty"` // Дерево Меркельразмер
}
func (this *BlockChain) String() string {
marshal, _ := json.Marshal(this)
return string(marshal)
}
func NewBlockChain(treeSize int64) *BlockChain {
return &BlockChain{TreeSize: treeSize, Version: "V1.0.0"}
}
func hash(node BlockNode) string {
sum256 := sha256.Sum256([]byte(node.String()))
return fmt.Sprintf("0x%x", sum256)
}
Здесь мы используем Sprintf для шестнадцатеричного вывода.
Хорошо, вот основная часть, дальше — более важная часть.
здесь,мы должны обратить внимание,Дерево Хэш Меркелиза и два вида структуры данных,Один из них — информационный прототип., другой - Дерево Меркельлист,Поэтому мы используем единый интерфейс для реализации,Затем мы трансформируем вниз
func hash(o1 common.Object, o2 common.Object) string {
t1 := ""
if o1 != nil {
te, f := (o1).(*Message)
if f {
let := *te
t1 = let.String()
//fmt.Println(let, "=", t1)
} else {
let := *(o1).(*MerkalNode)
t1 = let.String()
//fmt.Println(let, "=", t1)
}
}
t2 := ""
if o2 != nil {
te, f := (o2).(*Message)
if f {
let := *te
t2 = let.String()
//fmt.Println(let, "=", t2)
} else {
let := *(o2).(*MerkalNode)
t2 = let.String()
//fmt.Println(let, "=", t2)
}
}
//fmt.Println("Хеширование", t1, t2)
bytes := []byte(t1 + t2)
sum256 := sha256.Sum256(bytes)
return fmt.Sprintf("0x%x", string(sum256[:]))
}
func (this *MerkalTree) Valid(other MerkalTree) (*Message, bool) {
if this.RootHash == other.RootHash {
return nil, true
}
return this.RootNode.Valid(other.RootNode, this.DataSet, other.DataSet)
}
func (this *MerkalNode) Valid(other *MerkalNode, oldData *[]Message, newData *[]Message) (*Message, bool) {
if this.Equals(other) {
return nil, true
}
if this.Left != nil && other.Left == nil {
return nil, false
}
if (this.Right != nil && other.Right == nil) || (this.Right == nil && other.Right != nil) {
return nil, false
}
if this.Left == nil && other.Left != nil {
return nil, true
}
if this.Left == nil && this.Right == nil && this.Rp != -1 && this.Lp != -1 {
LpMsg := (*newData)[other.Lp]
if this.Lp != other.Lp {
return &LpMsg, false
}
RpMsg := (*newData)[other.Rp]
if this.Rp != other.Rp {
return &RpMsg, false
}
oldLpMsg := (*oldData)[other.Lp]
if LpMsg != oldLpMsg {
return &LpMsg, false
}
oldRpMsg := (*oldData)[other.Rp]
if RpMsg != oldRpMsg {
return &RpMsg, false
}
}
valid, b := this.Left.Valid(other.Left, oldData, newData)
if !b {
return valid, b
}
node, b2 := this.Right.Valid(other.Right, oldData, newData)
if !b2 {
return node, b2
}
return nil, true
}
На самом деле это двоичное разделение. Нам нужно только рекурсивно найти последний узел ошибки. Однако с этим кодом есть проблема, обнаружил ли кто-нибудь это?
Например, есть такой узел
С B проблем нет, с C проблем нет, но что делать, если во время передачи клиента в A возникает ошибка?
Итак, здесь нам также необходимо проверить текущий узел
Окончательный код выглядит следующим образом
if b && b2 && !this.Equals(other) {
return nil, false
}
Здесь мы не будем возвращаться к тому, какой узел сообщил об ошибке, потому что это бессмысленно.
Если вам нужно вернуться, сделайте абстракцию интерфейса самостоятельно, хаха, окончательный код
func (this *MerkalNode) Valid(other *MerkalNode, oldData *[]Message, newData *[]Message) (*Message, bool) {
if this.Equals(other) {
return nil, true
}
if this.Left != nil && other.Left == nil {
return nil, false
}
if (this.Right != nil && other.Right == nil) || (this.Right == nil && other.Right != nil) {
return nil, false
}
if this.Left == nil && other.Left != nil {
return nil, true
}
if this.Left == nil && this.Right == nil && this.Rp != -1 && this.Lp != -1 {
LpMsg := (*newData)[other.Lp]
if this.Lp != other.Lp {
return &LpMsg, false
}
RpMsg := (*newData)[other.Rp]
if this.Rp != other.Rp {
return &RpMsg, false
}
oldLpMsg := (*oldData)[other.Lp]
if LpMsg != oldLpMsg {
return &LpMsg, false
}
oldRpMsg := (*oldData)[other.Rp]
if RpMsg != oldRpMsg {
return &RpMsg, false
}
}
valid, b := this.Left.Valid(other.Left, oldData, newData)
if !b {
return valid, b
}
node, b2 := this.Right.Valid(other.Right, oldData, newData)
if !b2 {
return node, b2
}
if b && b2 && !this.Equals(other) {
return nil, false
}
return nil, true
}
Это легко, я напишу их все вместе. Конкретный исходный код вы можете посмотреть в моем репозитории на GitHub, хе-хе.
func (this *BlockChain) String() string {
marshal, _ := json.Marshal(this)
return string(marshal)
}
func NewBlockChain(treeSize int64) *BlockChain {
return &BlockChain{TreeSize: treeSize, Version: "V1.0.0"}
}
func hash(node BlockNode) string {
sum256 := sha256.Sum256([]byte(node.String()))
return fmt.Sprintf("0x%x", sum256)
}
func (this *BlockChain) AddMsg(message *merkal.Message) {
nodeLen := 0
Lock.Lock()
nodeLen = len(this.Nodes)
defer Lock.Unlock()
if nodeLen == 0 {
this.AddNode(NewBlockNode("head", this.Version, merkal.MerkalTree{}))
nodeLen = len(this.Nodes)
}
prev := this.Nodes[nodeLen-1]
useSize := prev.RootTree.Size()
if useSize < this.TreeSize {
prev.RootTree.AddNode(message)
return
}
tree := new(merkal.MerkalTree)
tree.AddNode(message)
node := NewBlockNode(hash(*prev), this.Version, *tree)
this.AddNode(node)
}
func (this *BlockChain) AddNode(node *BlockNode) {
defer func() {
a := recover()
if a != nil {
fmt.Println(a)
}
}()
if node.Version != this.Version {
panic("номер версиинеправильный")
}
nodeLen := len(this.Nodes)
if nodeLen == 0 {
this.Nodes = append(this.Nodes, NewBlockNode("head", this.Version, merkal.MerkalTree{}))
nodeLen = len(this.Nodes)
}
prev := this.Nodes[nodeLen-1]
node.PrevHash = hash(*prev)
this.Nodes = append(this.Nodes, node)
}
func NewBlockNode(prevHash string, version string, rootTree merkal.MerkalTree) *BlockNode {
node := &BlockNode{PrevHash: prevHash,
Version: version,
Datestamp: long(time.Now().UnixMilli()),
RootTree: rootTree,
Nonce: getNonce(),
}
node.Build()
return node
}
func getNonce() string {
return string("test")
}
func (this *BlockNode) Build() {
this.RootTreeCode = this.RootTree.RootHash
this.LocalTreeSize = this.RootTree.Size()
}
func (this *BlockNode) Valid(node *BlockNode) bool {
defer func() bool {
err := recover()
if err != nil {
log.Println(err)
return false
}
return true
}()
this.Build()
node.Build()
localVersion, localSize := node.Version, node.LocalTreeSize
if this.Version != localVersion || this.LocalTreeSize != localSize {
return false
}
valid, b := this.RootTree.Valid(node.RootTree)
if b {
return true
} else {
panic(valid.String())
}
}
func (this *BlockNode) String() string {
this.LocalTreeSize = this.RootTree.Size()
jsonBytes, _ := json.Marshal(this)
return string(jsonBytes)
}
func (this *MerkalNode) String() string {
marshal, _ := json.Marshal(this)
return string(marshal)
}
func (this *MerkalNode) Equals(other *MerkalNode) bool {
if other == nil {
return false
}
return reflect.DeepEqual(this, other)
}
func NewMerkalNode(Left *MerkalNode, Right *MerkalNode, Hash string, Lp int, Rp int) *MerkalNode {
res := &MerkalNode{Left: Left, Right: Right, Hash: Hash, Lp: Lp, Rp: Rp}
//fmt.Println(res)
return res
}
func getSize(node *MerkalNode, size int64) int64 {
if node == nil {
return size
}
size = getSize(node.Left, size)
size++
size = getSize(node.Right, size)
return size
}
func (this MerkalTree) Size() int64 {
return getSize(this.RootNode, 0)
}
func (this *MerkalTree) AddNode(data *Message) {
if this.DataSet == nil {
this.DataSet = (new([]Message))
}
messages := append((*this.DataSet), *data)
this.DataSet = (&messages)
this.Detail()
}
func (this *MerkalTree) String() string {
res, _ := json.Marshal(this)
return string(res)
}
func (this *MerkalTree) binaryDetail(data *[]Message, l int, r int) (resNode *MerkalNode) {
if r == l {
resNode = NewMerkalNode(nil, nil, hash(&(*data)[l], nil), l, r)
return resNode
}
if r-l == 1 {
resNode = NewMerkalNode(nil, nil, hash(&(*data)[l], &(*data)[r]), l, r)
return resNode
}
mid := (l-r)>>1 + r
left := this.binaryDetail(data, l, mid)
right := this.binaryDetail(data, mid+1, r)
resNode = NewMerkalNode(left, right, hash(left, right), -1, -1)
return resNode
}
func (this *MerkalTree) Detail() {
data := this.DataSet
l := 0
r := len(*data) - 1
detail := this.binaryDetail(data, l, r)
this.RootNode = detail
this.RootHash = (hash(detail, nil))
}
func NewMerkalTree(dataSet *[]Message) (res *MerkalTree) {
res = &MerkalTree{DataSet: dataSet}
res.RootHash = hash(res, nil)
return res
}
func (this *Message) String() string {
res, _ := json.Marshal(this)
return string(res)
}
func (this *Message) Equals(other Message) bool {
return reflect.DeepEqual(this, other)
}
type Object interface {
String() string
}
Вышеупомянутое содержание — это то, чем я поделюсь в этом выпуске. Я опубликую информацию о Raft и bftp после того, как попрактикуюсь в них в будущем. Вы также можете проверить etcd, чтобы расширить свои знания.
исходный кодgithubадрес:https://github.com/Karosown/blockLink.git
В настоящее время алгоритмы raft и bftp еще не реализованы, и каждый может работать вместе над улучшением этого проекта.