gpt4 book ai didi

data-structures - 在 Go 中实现 Merkle 树数据结构

转载 作者:IT王子 更新时间:2023-10-29 00:40:35 25 4
gpt4 key购买 nike

我目前正尝试在 Go 中实现 merkle-tree 数据结构。基本上,我的最终目标是存储一小组结构化数据(最大 10MB)并允许这个“数据库”轻松地与分布在网络上的其他节点同步(参见相关资料)。由于没有类型检查,我已经在 Node 中相当有效地实现了这一点。这就是 Go 的问题所在,我想利用 Go 的编译时类型检查,尽管我也想拥有一个可以与任何提供的树一起工作的库。

简而言之,我想将结构用作 merkle 节点,并且我希望拥有一个嵌入所有类型的 Merkle.Update() 方法。我试图避免为每个结构编写 Update()(尽管我知道这可能是唯一/最好的方法)。

我的想法是使用嵌入式类型:

//library
type Merkle struct {
Initialised bool
Container interface{} //in example this references foo
Fields []reflect.Type
//... other merkle state
}
//Merkle methods... Update()... etc...

//userland
type Foo struct {
Merkle
A int
B bool
C string
D map[string]*Bazz
E []*Bar
}

type Bazz struct {
Merkle
S int
T int
U int
}

type Bar struct {
Merkle
X int
Y int
Z int
}

在此示例中,Foo 将是根,其中将包含 BazzBar。这种关系可以通过反射(reflection)类型来推断。问题是用法:

foo := &Foo{
A: 42,
B: true,
C: "foo",
D: map[string]*Bazz{
"b1": &Bazz{},
"b2": &Bazz{},
},
E: []*Bar{
&Bar{},
&Bar{},
&Bar{},
},
}

merkle.Init(foo)
foo.Hash //Initial hash => abc...

foo.A = 35
foo.E = append(foo.E, &Bar{})

foo.Update()
foo.Hash //Updated hash => def...

我认为我们需要 merkle.Init(foo) 因为 foo.Init() 实际上是 foo.Merkle.Init() 并且无法反射(reflect)在 foo 上。未初始化的 BarBazz 可以被父级 foo.Update() 检测和初始化。一些反射(reflection)是可以接受的,因为目前正确性比性能更重要。另一个问题是,当我们 Update() 一个节点时,所有结构字段(子节点)也需要被 Update()d(重新散列),因为我们不是不确定发生了什么变化。我们可以执行 foo.SetInt("A", 35) 来实现自动更新,尽管这样我们就失去了编译时类型检查。

这会被认为是地道的 Go 语言吗?如果没有,如何改进?谁能想出一种替代方法来将数据集存储在内存中(用于快速读取)并进行简洁的数据集比较(用于通过网络进行高效的增量传输)?编辑:还有一个元问题:问这种问题的最佳地点在哪里,StackOverflow、Reddit 还是 go-nuts?最初发布于 reddit没有答案:(

最佳答案

一些目标看起来像:

  • Hash anything -- 通过开箱即用地散列大量内容使其易于使用
  • 缓存散列 -- 使更新只是重新散列他们需要的内容
  • 是地道的 -- 很好地融入其他 Go 代码

我认为您可以大致像内置的 encoding/gobencoding/json 等序列化工具那样攻击散列,这是三管齐下的:如果类型实现它,则使用特殊方法(对于 MarshalJSON 的 JSON),对基本类型使用类型切换,并使用反射回退到令人讨厌的默认情况。这是一个 API 草图,它为哈希缓存提供了一个帮助程序,并允许类型实现或不实现 Hash:

package merkle

type HashVal uint64

const MissingHash HashVal = 0

// Hasher provides a custom hash implementation for a type. Not
// everything needs to implement it, but doing so can speed
// updates.
type Hasher interface {
Hash() HashVal
}

// HashCacher is the interface for items that cache a hash value.
// Normally implemented by embedding HashCache.
type HashCacher interface {
CachedHash() *HashVal
}

// HashCache implements HashCacher; it's meant to be embedded in your
// structs to make updating hash trees more efficient.
type HashCache struct {
h HashVal
}

// CachedHash implements HashCacher.
func (h *HashCache) CachedHash() *HashVal {
return &h.h
}

// Hash returns something's hash, using a cached hash or Hash() method if
// available.
func Hash(i interface{}) HashVal {
if hashCacher, ok := i.(HashCacher); ok {
if cached := *hashCacher.CachedHash(); cached != MissingHash {
return cached
}
}

switch i := i.(type) {
case Hasher:
return i.Hash()
case uint64:
return HashVal(i * 8675309) // or, you know, use a real hash
case []byte:
// CRC the bytes, say
return 0xdeadbeef
default:
return 0xdeadbeef
// terrible slow recursive case using reflection
// like: iterate fields using reflect, then hash each
}

// instead of panic()ing here, you could live a little
// dangerously and declare that changes to unhashable
// types don't invalidate the tree
panic("unhashable type passed to Hash()")
}

// Item is a node in the Merkle tree, which must know how to find its
// parent Item (the root node should return nil) and should usually
// embed HashCache for efficient updates. To avoid using reflection,
// Items might benefit from being Hashers as well.
type Item interface {
Parent() Item
HashCacher
}

// Update updates the chain of items between i and the root, given the
// leaf node that may have been changed.
func Update(i Item) {
for i != nil {
cached := i.CachedHash()
*cached = MissingHash // invalidate
*cached = Hash(i)
i = i.Parent()
}
}

关于data-structures - 在 Go 中实现 Merkle 树数据结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25495896/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com