introduce ramTrie
This commit is contained in:
parent
d7f97ab750
commit
f829fb6206
11 changed files with 334 additions and 115 deletions
|
@ -46,7 +46,7 @@ type ClaimTrie struct {
|
||||||
nodeManager node.Manager
|
nodeManager node.Manager
|
||||||
|
|
||||||
// Prefix tree (trie) that manages merkle hash of each node.
|
// Prefix tree (trie) that manages merkle hash of each node.
|
||||||
merkleTrie *merkletrie.MerkleTrie
|
merkleTrie merkletrie.MerkleTrie
|
||||||
|
|
||||||
// Current block height, which is increased by one when AppendBlock() is called.
|
// Current block height, which is increased by one when AppendBlock() is called.
|
||||||
height int32
|
height int32
|
||||||
|
@ -99,6 +99,11 @@ func New(cfg config.Config) (*ClaimTrie, error) {
|
||||||
trie := merkletrie.New(nodeManager, trieRepo)
|
trie := merkletrie.New(nodeManager, trieRepo)
|
||||||
cleanups = append(cleanups, trie.Close)
|
cleanups = append(cleanups, trie.Close)
|
||||||
|
|
||||||
|
persistentTrie := merkletrie.NewPersistentTrie(nodeManager, trieRepo)
|
||||||
|
cleanups = append(cleanups, persistentTrie.Close)
|
||||||
|
trie = persistentTrie
|
||||||
|
}
|
||||||
|
|
||||||
// Restore the last height.
|
// Restore the last height.
|
||||||
previousHeight, err := blockRepo.Load()
|
previousHeight, err := blockRepo.Load()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -110,12 +115,11 @@ func New(cfg config.Config) (*ClaimTrie, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("get hash: %w", err)
|
return nil, fmt.Errorf("get hash: %w", err)
|
||||||
}
|
}
|
||||||
trie.SetRoot(hash)
|
|
||||||
|
|
||||||
_, err = nodeManager.IncrementHeightTo(previousHeight)
|
_, err = nodeManager.IncrementHeightTo(previousHeight)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("node manager init: %w", err)
|
return nil, fmt.Errorf("node manager init: %w", err)
|
||||||
}
|
}
|
||||||
|
trie.SetRoot(hash, nil) // keep this after IncrementHeightTo
|
||||||
}
|
}
|
||||||
|
|
||||||
ct := &ClaimTrie{
|
ct := &ClaimTrie{
|
||||||
|
@ -275,7 +279,7 @@ func (ct *ClaimTrie) AppendBlock() error {
|
||||||
ct.blockRepo.Set(ct.height, h)
|
ct.blockRepo.Set(ct.height, h)
|
||||||
|
|
||||||
if hitFork {
|
if hitFork {
|
||||||
ct.merkleTrie.SetRoot(h) // for clearing the memory entirely
|
ct.merkleTrie.SetRoot(h, names) // for clearing the memory entirely
|
||||||
runtime.GC()
|
runtime.GC()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,12 +341,21 @@ func (ct *ClaimTrie) ResetHeight(height int32) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
passedHashFork := ct.height >= param.AllClaimsInMerkleForkHeight && height < param.AllClaimsInMerkleForkHeight
|
||||||
ct.height = height
|
ct.height = height
|
||||||
hash, err := ct.blockRepo.Get(height)
|
hash, err := ct.blockRepo.Get(height)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ct.merkleTrie.SetRoot(hash)
|
|
||||||
|
if passedHashFork {
|
||||||
|
names = nil // force them to reconsider all names
|
||||||
|
}
|
||||||
|
ct.merkleTrie.SetRoot(hash, names)
|
||||||
|
|
||||||
|
if !ct.MerkleHash().IsEqual(hash) {
|
||||||
|
return fmt.Errorf("unable to restore the hash at height %d", height)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -231,3 +231,39 @@ func verifyBestIndex(t *testing.T, ct *ClaimTrie, name string, idx uint32, claim
|
||||||
r.Equal(idx, n.BestClaim.OutPoint.Index)
|
r.Equal(idx, n.BestClaim.OutPoint.Index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRebuild(t *testing.T) {
|
||||||
|
r := require.New(t)
|
||||||
|
setup(t)
|
||||||
|
ct, err := New(true)
|
||||||
|
r.NoError(err)
|
||||||
|
r.NotNil(ct)
|
||||||
|
defer func() {
|
||||||
|
err := ct.Close()
|
||||||
|
r.NoError(err)
|
||||||
|
}()
|
||||||
|
|
||||||
|
hash := chainhash.HashH([]byte{1, 2, 3})
|
||||||
|
|
||||||
|
o1 := wire.OutPoint{Hash: hash, Index: 1}
|
||||||
|
err = ct.AddClaim([]byte("test1"), o1, node.NewClaimID(o1), 1, nil)
|
||||||
|
r.NoError(err)
|
||||||
|
|
||||||
|
o2 := wire.OutPoint{Hash: hash, Index: 2}
|
||||||
|
err = ct.AddClaim([]byte("test2"), o2, node.NewClaimID(o2), 2, nil)
|
||||||
|
r.NoError(err)
|
||||||
|
|
||||||
|
err = ct.AppendBlock()
|
||||||
|
r.NoError(err)
|
||||||
|
|
||||||
|
m := ct.MerkleHash()
|
||||||
|
r.NotNil(m)
|
||||||
|
r.NotEqual(*merkletrie.EmptyTrieHash, *m)
|
||||||
|
|
||||||
|
ct.merkleTrie = merkletrie.NewRamTrie(ct.nodeManager)
|
||||||
|
ct.merkleTrie.SetRoot(m, nil)
|
||||||
|
|
||||||
|
m2 := ct.MerkleHash()
|
||||||
|
r.NotNil(m2)
|
||||||
|
r.Equal(*m, *m2)
|
||||||
|
}
|
||||||
|
|
|
@ -135,9 +135,9 @@ var blockNameCmd = &cobra.Command{
|
||||||
return fmt.Errorf("can't open merkle trie repo: %w", err)
|
return fmt.Errorf("can't open merkle trie repo: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
trie := merkletrie.New(nil, trieRepo)
|
trie := merkletrie.NewPersistentTrie(nil, trieRepo)
|
||||||
defer trie.Close()
|
defer trie.Close()
|
||||||
trie.SetRoot(hash)
|
trie.SetRoot(hash, nil)
|
||||||
if len(args) > 1 {
|
if len(args) > 1 {
|
||||||
trie.Dump(args[1], param.AllClaimsInMerkleForkHeight >= int32(height))
|
trie.Dump(args[1], param.AllClaimsInMerkleForkHeight >= int32(height))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
package merkletrie
|
package merkletrie
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/lbryio/chain/chaincfg/chainhash"
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
)
|
)
|
||||||
|
|
||||||
type KeyType []byte
|
type KeyType []byte
|
||||||
|
|
||||||
type PrefixTrieNode struct { // implements sort.Interface
|
type collapsedVertex struct { // implements sort.Interface
|
||||||
children []*PrefixTrieNode
|
children []*collapsedVertex
|
||||||
key KeyType
|
key KeyType
|
||||||
hash *chainhash.Hash
|
merkleHash *chainhash.Hash
|
||||||
hasClaims bool
|
claimHash *chainhash.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
// insertAt inserts v into s at index i and returns the new slice.
|
// insertAt inserts v into s at index i and returns the new slice.
|
||||||
// https://stackoverflow.com/questions/42746972/golang-insert-to-a-sorted-slice
|
// https://stackoverflow.com/questions/42746972/golang-insert-to-a-sorted-slice
|
||||||
func insertAt(data []*PrefixTrieNode, i int, v *PrefixTrieNode) []*PrefixTrieNode {
|
func insertAt(data []*collapsedVertex, i int, v *collapsedVertex) []*collapsedVertex {
|
||||||
if i == len(data) {
|
if i == len(data) {
|
||||||
// Insert at end is the easy case.
|
// Insert at end is the easy case.
|
||||||
return append(data, v)
|
return append(data, v)
|
||||||
|
@ -30,7 +30,7 @@ func insertAt(data []*PrefixTrieNode, i int, v *PrefixTrieNode) []*PrefixTrieNod
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ptn *PrefixTrieNode) Insert(value *PrefixTrieNode) *PrefixTrieNode {
|
func (ptn *collapsedVertex) Insert(value *collapsedVertex) *collapsedVertex {
|
||||||
// keep it sorted (and sort.Sort is too slow)
|
// keep it sorted (and sort.Sort is too slow)
|
||||||
index := sortSearch(ptn.children, value.key[0])
|
index := sortSearch(ptn.children, value.key[0])
|
||||||
ptn.children = insertAt(ptn.children, index, value)
|
ptn.children = insertAt(ptn.children, index, value)
|
||||||
|
@ -40,7 +40,7 @@ func (ptn *PrefixTrieNode) Insert(value *PrefixTrieNode) *PrefixTrieNode {
|
||||||
|
|
||||||
// this sort.Search is stolen shamelessly from search.go,
|
// this sort.Search is stolen shamelessly from search.go,
|
||||||
// and modified for performance to not need a closure
|
// and modified for performance to not need a closure
|
||||||
func sortSearch(nodes []*PrefixTrieNode, b byte) int {
|
func sortSearch(nodes []*collapsedVertex, b byte) int {
|
||||||
i, j := 0, len(nodes)
|
i, j := 0, len(nodes)
|
||||||
for i < j {
|
for i < j {
|
||||||
h := int(uint(i+j) >> 1) // avoid overflow when computing h
|
h := int(uint(i+j) >> 1) // avoid overflow when computing h
|
||||||
|
@ -55,9 +55,9 @@ func sortSearch(nodes []*PrefixTrieNode, b byte) int {
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ptn *PrefixTrieNode) FindNearest(start KeyType) (int, *PrefixTrieNode) {
|
func (ptn *collapsedVertex) findNearest(key KeyType) (int, *collapsedVertex) {
|
||||||
// none of the children overlap on the first char or we would have a parent node with that char
|
// none of the children overlap on the first char or we would have a parent node with that char
|
||||||
index := sortSearch(ptn.children, start[0])
|
index := sortSearch(ptn.children, key[0])
|
||||||
hits := ptn.children[index:]
|
hits := ptn.children[index:]
|
||||||
if len(hits) > 0 {
|
if len(hits) > 0 {
|
||||||
return index, hits[0]
|
return index, hits[0]
|
||||||
|
@ -65,26 +65,17 @@ func (ptn *PrefixTrieNode) FindNearest(start KeyType) (int, *PrefixTrieNode) {
|
||||||
return -1, nil
|
return -1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type PrefixTrie interface {
|
type collapsedTrie struct {
|
||||||
InsertOrFind(value KeyType) (bool, *PrefixTrieNode)
|
Root *collapsedVertex
|
||||||
Find(value KeyType) *PrefixTrieNode
|
|
||||||
FindPath(value KeyType) ([]int, []*PrefixTrieNode)
|
|
||||||
IterateFrom(start KeyType, handler func(value *PrefixTrieNode) bool)
|
|
||||||
Erase(value KeyType) bool
|
|
||||||
NodeCount() int
|
|
||||||
}
|
|
||||||
|
|
||||||
type prefixTrie struct {
|
|
||||||
root *PrefixTrieNode
|
|
||||||
Nodes int
|
Nodes int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPrefixTrie() PrefixTrie {
|
func NewCollapsedTrie() *collapsedTrie {
|
||||||
// we never delete the root node
|
// we never delete the Root node
|
||||||
return &prefixTrie{root: &PrefixTrieNode{key: make(KeyType, 0)}, Nodes: 1}
|
return &collapsedTrie{Root: &collapsedVertex{key: make(KeyType, 0)}, Nodes: 1}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pt *prefixTrie) NodeCount() int {
|
func (pt *collapsedTrie) NodeCount() int {
|
||||||
return pt.Nodes
|
return pt.Nodes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,10 +92,11 @@ func matchLength(a, b KeyType) int {
|
||||||
return minLen
|
return minLen
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pt *prefixTrie) insert(value KeyType, node *PrefixTrieNode) (bool, *PrefixTrieNode) {
|
func (pt *collapsedTrie) insert(value KeyType, node *collapsedVertex) (bool, *collapsedVertex) {
|
||||||
index, child := node.FindNearest(value)
|
index, child := node.findNearest(value)
|
||||||
match := 0
|
match := 0
|
||||||
if index >= 0 { // if we found a child
|
if index >= 0 { // if we found a child
|
||||||
|
child.merkleHash = nil
|
||||||
match = matchLength(value, child.key)
|
match = matchLength(value, child.key)
|
||||||
if len(value) == match && len(child.key) == match {
|
if len(value) == match && len(child.key) == match {
|
||||||
return false, child
|
return false, child
|
||||||
|
@ -112,12 +104,12 @@ func (pt *prefixTrie) insert(value KeyType, node *PrefixTrieNode) (bool, *Prefix
|
||||||
}
|
}
|
||||||
if match <= 0 {
|
if match <= 0 {
|
||||||
pt.Nodes++
|
pt.Nodes++
|
||||||
return true, node.Insert(&PrefixTrieNode{key: value})
|
return true, node.Insert(&collapsedVertex{key: value})
|
||||||
}
|
}
|
||||||
if match < len(child.key) {
|
if match < len(child.key) {
|
||||||
grandChild := PrefixTrieNode{key: child.key[match:], children: child.children,
|
grandChild := collapsedVertex{key: child.key[match:], children: child.children,
|
||||||
hasClaims: child.hasClaims, hash: child.hash}
|
claimHash: child.claimHash, merkleHash: child.merkleHash}
|
||||||
newChild := PrefixTrieNode{key: child.key[0:match], children: []*PrefixTrieNode{&grandChild}}
|
newChild := collapsedVertex{key: child.key[0:match], children: []*collapsedVertex{&grandChild}}
|
||||||
child = &newChild
|
child = &newChild
|
||||||
node.children[index] = child
|
node.children[index] = child
|
||||||
pt.Nodes++
|
pt.Nodes++
|
||||||
|
@ -128,15 +120,21 @@ func (pt *prefixTrie) insert(value KeyType, node *PrefixTrieNode) (bool, *Prefix
|
||||||
return pt.insert(value[match:], child)
|
return pt.insert(value[match:], child)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pt *prefixTrie) InsertOrFind(value KeyType) (bool, *PrefixTrieNode) {
|
func (pt *collapsedTrie) InsertOrFind(value KeyType) (bool, *collapsedVertex) {
|
||||||
|
pt.Root.merkleHash = nil
|
||||||
if len(value) <= 0 {
|
if len(value) <= 0 {
|
||||||
return false, pt.root
|
return false, pt.Root
|
||||||
}
|
}
|
||||||
return pt.insert(value, pt.root)
|
|
||||||
|
// we store the name so we need to make our own copy of it
|
||||||
|
// this avoids errors where this function is called via the DB iterator
|
||||||
|
v2 := make([]byte, len(value))
|
||||||
|
copy(v2, value)
|
||||||
|
return pt.insert(v2, pt.Root)
|
||||||
}
|
}
|
||||||
|
|
||||||
func find(value KeyType, node *PrefixTrieNode, pathIndexes *[]int, path *[]*PrefixTrieNode) *PrefixTrieNode {
|
func find(value KeyType, node *collapsedVertex, pathIndexes *[]int, path *[]*collapsedVertex) *collapsedVertex {
|
||||||
index, child := node.FindNearest(value)
|
index, child := node.findNearest(value)
|
||||||
if index < 0 {
|
if index < 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -162,34 +160,36 @@ func find(value KeyType, node *PrefixTrieNode, pathIndexes *[]int, path *[]*Pref
|
||||||
return find(value[match:], child, pathIndexes, path)
|
return find(value[match:], child, pathIndexes, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pt *prefixTrie) Find(value KeyType) *PrefixTrieNode {
|
func (pt *collapsedTrie) Find(value KeyType) *collapsedVertex {
|
||||||
if len(value) <= 0 {
|
if len(value) <= 0 {
|
||||||
return pt.root
|
return pt.Root
|
||||||
}
|
}
|
||||||
return find(value, pt.root, nil, nil)
|
return find(value, pt.Root, nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pt *prefixTrie) FindPath(value KeyType) ([]int, []*PrefixTrieNode) {
|
func (pt *collapsedTrie) FindPath(value KeyType) ([]int, []*collapsedVertex) {
|
||||||
pathIndexes := []int{-1}
|
pathIndexes := []int{-1}
|
||||||
path := []*PrefixTrieNode{pt.root}
|
path := []*collapsedVertex{pt.Root}
|
||||||
result := find(value, pt.root, &pathIndexes, &path)
|
if len(value) > 0 {
|
||||||
if result == nil {
|
result := find(value, pt.Root, &pathIndexes, &path)
|
||||||
return nil, nil
|
if result == nil { // not sure I want this line
|
||||||
} // not sure I want this line
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
return pathIndexes, path
|
return pathIndexes, path
|
||||||
}
|
}
|
||||||
|
|
||||||
// IterateFrom can be used to find a value and run a function on that value.
|
// IterateFrom can be used to find a value and run a function on that value.
|
||||||
// If the handler returns true it continues to iterate through the children of value.
|
// If the handler returns true it continues to iterate through the children of value.
|
||||||
func (pt *prefixTrie) IterateFrom(start KeyType, handler func(value *PrefixTrieNode) bool) {
|
func (pt *collapsedTrie) IterateFrom(start KeyType, handler func(value *collapsedVertex) bool) {
|
||||||
node := find(start, pt.root, nil, nil)
|
node := find(start, pt.Root, nil, nil)
|
||||||
if node == nil {
|
if node == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
iterateFrom(node, handler)
|
iterateFrom(node, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func iterateFrom(node *PrefixTrieNode, handler func(value *PrefixTrieNode) bool) {
|
func iterateFrom(node *collapsedVertex, handler func(value *collapsedVertex) bool) {
|
||||||
for handler(node) {
|
for handler(node) {
|
||||||
for _, child := range node.children {
|
for _, child := range node.children {
|
||||||
iterateFrom(child, handler)
|
iterateFrom(child, handler)
|
||||||
|
@ -197,19 +197,25 @@ func iterateFrom(node *PrefixTrieNode, handler func(value *PrefixTrieNode) bool)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pt *prefixTrie) Erase(value KeyType) bool {
|
func (pt *collapsedTrie) Erase(value KeyType) bool {
|
||||||
indexes, path := pt.FindPath(value)
|
indexes, path := pt.FindPath(value)
|
||||||
if path == nil || len(path) <= 1 {
|
if path == nil || len(path) <= 1 {
|
||||||
|
if len(path) == 1 {
|
||||||
|
path[0].merkleHash = nil
|
||||||
|
path[0].claimHash = nil
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
nodes := pt.Nodes
|
nodes := pt.Nodes
|
||||||
for i := len(path) - 1; i > 0; i-- {
|
i := len(path) - 1
|
||||||
|
path[i].claimHash = nil // this is the thing we are erasing; the rest is book-keeping
|
||||||
|
for ; i > 0; i-- {
|
||||||
childCount := len(path[i].children)
|
childCount := len(path[i].children)
|
||||||
noClaimData := !path[i].hasClaims
|
noClaimData := path[i].claimHash == nil
|
||||||
|
path[i].merkleHash = nil
|
||||||
if childCount == 1 && noClaimData {
|
if childCount == 1 && noClaimData {
|
||||||
path[i].key = append(path[i].key, path[i].children[0].key...)
|
path[i].key = append(path[i].key, path[i].children[0].key...)
|
||||||
path[i].hash = nil
|
path[i].claimHash = path[i].children[0].claimHash
|
||||||
path[i].hasClaims = path[i].children[0].hasClaims
|
|
||||||
path[i].children = path[i].children[0].children
|
path[i].children = path[i].children[0].children
|
||||||
pt.Nodes--
|
pt.Nodes--
|
||||||
continue
|
continue
|
||||||
|
@ -222,5 +228,8 @@ func (pt *prefixTrie) Erase(value KeyType) bool {
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
for ; i >= 0; i-- {
|
||||||
|
path[i].merkleHash = nil
|
||||||
|
}
|
||||||
return nodes > pt.Nodes
|
return nodes > pt.Nodes
|
||||||
}
|
}
|
|
@ -9,10 +9,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func b(value string) []byte { return []byte(value) }
|
func b(value string) []byte { return []byte(value) }
|
||||||
func eq(x []byte, y string) bool { return bytes.Compare(x, b(y)) == 0 }
|
func eq(x []byte, y string) bool { return bytes.Equal(x, b(y)) }
|
||||||
|
|
||||||
func TestInsertAndErase(t *testing.T) {
|
func TestInsertAndErase(t *testing.T) {
|
||||||
trie := NewPrefixTrie()
|
trie := NewCollapsedTrie()
|
||||||
assert.True(t, trie.NodeCount() == 1)
|
assert.True(t, trie.NodeCount() == 1)
|
||||||
inserted, node := trie.InsertOrFind(b("abc"))
|
inserted, node := trie.InsertOrFind(b("abc"))
|
||||||
assert.True(t, inserted)
|
assert.True(t, inserted)
|
||||||
|
@ -45,8 +45,28 @@ func TestInsertAndErase(t *testing.T) {
|
||||||
assert.Equal(t, 1, trie.NodeCount())
|
assert.Equal(t, 1, trie.NodeCount())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPrefixTrie(t *testing.T) {
|
func TestNilNameHandling(t *testing.T) {
|
||||||
inserts := 1000000
|
trie := NewCollapsedTrie()
|
||||||
|
inserted, n := trie.InsertOrFind([]byte("test"))
|
||||||
|
assert.True(t, inserted)
|
||||||
|
n.claimHash = EmptyTrieHash
|
||||||
|
inserted, n = trie.InsertOrFind(nil)
|
||||||
|
assert.False(t, inserted)
|
||||||
|
n.claimHash = EmptyTrieHash
|
||||||
|
n.merkleHash = EmptyTrieHash
|
||||||
|
inserted, n = trie.InsertOrFind(nil)
|
||||||
|
assert.False(t, inserted)
|
||||||
|
assert.NotNil(t, n.claimHash)
|
||||||
|
assert.Nil(t, n.merkleHash)
|
||||||
|
nodeRemoved := trie.Erase(nil)
|
||||||
|
assert.False(t, nodeRemoved)
|
||||||
|
inserted, n = trie.InsertOrFind(nil)
|
||||||
|
assert.False(t, inserted)
|
||||||
|
assert.Nil(t, n.claimHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCollapsedTriePerformance(t *testing.T) {
|
||||||
|
inserts := 10000 // increase this to 1M for more interesting results
|
||||||
data := make([][]byte, inserts)
|
data := make([][]byte, inserts)
|
||||||
rand.Seed(42)
|
rand.Seed(42)
|
||||||
for i := 0; i < inserts; i++ {
|
for i := 0; i < inserts; i++ {
|
||||||
|
@ -58,21 +78,21 @@ func TestPrefixTrie(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trie := NewPrefixTrie()
|
trie := NewCollapsedTrie()
|
||||||
// doing my own timing because I couldn't get the B.Run method to work:
|
// doing my own timing because I couldn't get the B.Run method to work:
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
for i := 0; i < inserts; i++ {
|
for i := 0; i < inserts; i++ {
|
||||||
_, node := trie.InsertOrFind(data[i])
|
_, node := trie.InsertOrFind(data[i])
|
||||||
assert.NotNil(t, node, "Failure at %d of %d", i, inserts)
|
assert.NotNil(t, node, "Failure at %d of %d", i, inserts)
|
||||||
}
|
}
|
||||||
t.Logf("Insertion in %f sec.", time.Now().Sub(start).Seconds())
|
t.Logf("Insertion in %f sec.", time.Since(start).Seconds())
|
||||||
|
|
||||||
start = time.Now()
|
start = time.Now()
|
||||||
for i := 0; i < inserts; i++ {
|
for i := 0; i < inserts; i++ {
|
||||||
node := trie.Find(data[i])
|
node := trie.Find(data[i])
|
||||||
assert.True(t, bytes.HasSuffix(data[i], node.key), "Failure on %d of %d", i, inserts)
|
assert.True(t, bytes.HasSuffix(data[i], node.key), "Failure on %d of %d", i, inserts)
|
||||||
}
|
}
|
||||||
t.Logf("Lookup in %f sec. on %d nodes.", time.Now().Sub(start).Seconds(), trie.NodeCount())
|
t.Logf("Lookup in %f sec. on %d nodes.", time.Since(start).Seconds(), trie.NodeCount())
|
||||||
|
|
||||||
start = time.Now()
|
start = time.Now()
|
||||||
for i := 0; i < inserts; i++ {
|
for i := 0; i < inserts; i++ {
|
||||||
|
@ -81,12 +101,12 @@ func TestPrefixTrie(t *testing.T) {
|
||||||
assert.True(t, len(path) > 1)
|
assert.True(t, len(path) > 1)
|
||||||
assert.True(t, bytes.HasSuffix(data[i], path[len(path)-1].key))
|
assert.True(t, bytes.HasSuffix(data[i], path[len(path)-1].key))
|
||||||
}
|
}
|
||||||
t.Logf("Parents in %f sec.", time.Now().Sub(start).Seconds())
|
t.Logf("Parents in %f sec.", time.Since(start).Seconds())
|
||||||
|
|
||||||
start = time.Now()
|
start = time.Now()
|
||||||
for i := 0; i < inserts; i++ {
|
for i := 0; i < inserts; i++ {
|
||||||
trie.Erase(data[i])
|
trie.Erase(data[i])
|
||||||
}
|
}
|
||||||
t.Logf("Deletion in %f sec.", time.Now().Sub(start).Seconds())
|
t.Logf("Deletion in %f sec.", time.Since(start).Seconds())
|
||||||
assert.Equal(t, 1, trie.NodeCount())
|
assert.Equal(t, 1, trie.NodeCount())
|
||||||
}
|
}
|
|
@ -7,25 +7,27 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
"github.com/btcsuite/btcd/claimtrie/node"
|
||||||
|
|
||||||
"github.com/cockroachdb/pebble"
|
"github.com/cockroachdb/pebble"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// EmptyTrieHash represents the Merkle Hash of an empty MerkleTrie.
|
// EmptyTrieHash represents the Merkle Hash of an empty PersistentTrie.
|
||||||
// "0000000000000000000000000000000000000000000000000000000000000001"
|
// "0000000000000000000000000000000000000000000000000000000000000001"
|
||||||
EmptyTrieHash = &chainhash.Hash{1}
|
EmptyTrieHash = &chainhash.Hash{1}
|
||||||
NoChildrenHash = &chainhash.Hash{2}
|
NoChildrenHash = &chainhash.Hash{2}
|
||||||
NoClaimsHash = &chainhash.Hash{3}
|
NoClaimsHash = &chainhash.Hash{3}
|
||||||
)
|
)
|
||||||
|
|
||||||
// ValueStore enables MerkleTrie to query node values from different implementations.
|
// ValueStore enables PersistentTrie to query node values from different implementations.
|
||||||
type ValueStore interface {
|
type ValueStore interface {
|
||||||
ClaimHashes(name []byte) []*chainhash.Hash
|
|
||||||
Hash(name []byte) *chainhash.Hash
|
Hash(name []byte) *chainhash.Hash
|
||||||
|
IterateNames(predicate func(name []byte) bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MerkleTrie implements a 256-way prefix tree.
|
// PersistentTrie implements a 256-way prefix tree.
|
||||||
type MerkleTrie struct {
|
type PersistentTrie struct {
|
||||||
store ValueStore
|
store ValueStore
|
||||||
repo Repo
|
repo Repo
|
||||||
|
|
||||||
|
@ -33,10 +35,10 @@ type MerkleTrie struct {
|
||||||
bufs *sync.Pool
|
bufs *sync.Pool
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a MerkleTrie.
|
// NewPersistentTrie returns a PersistentTrie.
|
||||||
func New(store ValueStore, repo Repo) *MerkleTrie {
|
func NewPersistentTrie(store ValueStore, repo Repo) *PersistentTrie {
|
||||||
|
|
||||||
tr := &MerkleTrie{
|
tr := &PersistentTrie{
|
||||||
store: store,
|
store: store,
|
||||||
repo: repo,
|
repo: repo,
|
||||||
bufs: &sync.Pool{
|
bufs: &sync.Pool{
|
||||||
|
@ -50,14 +52,14 @@ func New(store ValueStore, repo Repo) *MerkleTrie {
|
||||||
return tr
|
return tr
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetRoot drops all resolved nodes in the MerkleTrie, and set the root with specified hash.
|
// SetRoot drops all resolved nodes in the PersistentTrie, and set the Root with specified hash.
|
||||||
func (t *MerkleTrie) SetRoot(h *chainhash.Hash) {
|
func (t *PersistentTrie) SetRoot(h *chainhash.Hash, names [][]byte) {
|
||||||
t.root = newVertex(h)
|
t.root = newVertex(h)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update updates the nodes along the path to the key.
|
// Update updates the nodes along the path to the key.
|
||||||
// Each node is resolved or created with their Hash cleared.
|
// Each node is resolved or created with their Hash cleared.
|
||||||
func (t *MerkleTrie) Update(name []byte, restoreChildren bool) {
|
func (t *PersistentTrie) Update(name []byte, restoreChildren bool) {
|
||||||
|
|
||||||
n := t.root
|
n := t.root
|
||||||
for i, ch := range name {
|
for i, ch := range name {
|
||||||
|
@ -80,7 +82,7 @@ func (t *MerkleTrie) Update(name []byte, restoreChildren bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolveChildLinks updates the links on n
|
// resolveChildLinks updates the links on n
|
||||||
func (t *MerkleTrie) resolveChildLinks(n *vertex, key []byte) {
|
func (t *PersistentTrie) resolveChildLinks(n *vertex, key []byte) {
|
||||||
|
|
||||||
if n.merkleHash == nil {
|
if n.merkleHash == nil {
|
||||||
return
|
return
|
||||||
|
@ -108,9 +110,9 @@ func (t *MerkleTrie) resolveChildLinks(n *vertex, key []byte) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MerkleHash returns the Merkle Hash of the MerkleTrie.
|
// MerkleHash returns the Merkle Hash of the PersistentTrie.
|
||||||
// All nodes must have been resolved before calling this function.
|
// All nodes must have been resolved before calling this function.
|
||||||
func (t *MerkleTrie) MerkleHash() *chainhash.Hash {
|
func (t *PersistentTrie) MerkleHash() *chainhash.Hash {
|
||||||
buf := make([]byte, 0, 256)
|
buf := make([]byte, 0, 256)
|
||||||
if h := t.merkle(buf, t.root); h == nil {
|
if h := t.merkle(buf, t.root); h == nil {
|
||||||
return EmptyTrieHash
|
return EmptyTrieHash
|
||||||
|
@ -120,7 +122,7 @@ func (t *MerkleTrie) MerkleHash() *chainhash.Hash {
|
||||||
|
|
||||||
// merkle recursively resolves the hashes of the node.
|
// merkle recursively resolves the hashes of the node.
|
||||||
// All nodes must have been resolved before calling this function.
|
// All nodes must have been resolved before calling this function.
|
||||||
func (t *MerkleTrie) merkle(prefix []byte, v *vertex) *chainhash.Hash {
|
func (t *PersistentTrie) merkle(prefix []byte, v *vertex) *chainhash.Hash {
|
||||||
if v.merkleHash != nil {
|
if v.merkleHash != nil {
|
||||||
return v.merkleHash
|
return v.merkleHash
|
||||||
}
|
}
|
||||||
|
@ -178,7 +180,7 @@ func keysInOrder(v *vertex) []byte {
|
||||||
return keys
|
return keys
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *MerkleTrie) MerkleHashAllClaims() *chainhash.Hash {
|
func (t *PersistentTrie) MerkleHashAllClaims() *chainhash.Hash {
|
||||||
buf := make([]byte, 0, 256)
|
buf := make([]byte, 0, 256)
|
||||||
if h := t.merkleAllClaims(buf, t.root); h == nil {
|
if h := t.merkleAllClaims(buf, t.root); h == nil {
|
||||||
return EmptyTrieHash
|
return EmptyTrieHash
|
||||||
|
@ -186,7 +188,7 @@ func (t *MerkleTrie) MerkleHashAllClaims() *chainhash.Hash {
|
||||||
return t.root.merkleHash
|
return t.root.merkleHash
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *MerkleTrie) merkleAllClaims(prefix []byte, v *vertex) *chainhash.Hash {
|
func (t *PersistentTrie) merkleAllClaims(prefix []byte, v *vertex) *chainhash.Hash {
|
||||||
if v.merkleHash != nil {
|
if v.merkleHash != nil {
|
||||||
return v.merkleHash
|
return v.merkleHash
|
||||||
}
|
}
|
||||||
|
@ -213,32 +215,25 @@ func (t *MerkleTrie) merkleAllClaims(prefix []byte, v *vertex) *chainhash.Hash {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var claimsHash *chainhash.Hash
|
|
||||||
if v.hasValue {
|
if v.hasValue {
|
||||||
claimsHash = v.claimsHash
|
if v.claimsHash == nil {
|
||||||
if claimsHash == nil {
|
v.claimsHash = t.store.Hash(prefix)
|
||||||
claimHashes := t.store.ClaimHashes(prefix)
|
v.hasValue = v.claimsHash != nil
|
||||||
if len(claimHashes) > 0 {
|
|
||||||
claimsHash = computeMerkleRoot(claimHashes)
|
|
||||||
v.claimsHash = claimsHash
|
|
||||||
} else {
|
|
||||||
v.hasValue = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(childHashes) > 1 || claimsHash != nil { // yeah, about that 1 there -- old code used the condensed trie
|
if len(childHashes) > 1 || v.claimsHash != nil { // yeah, about that 1 there -- old code used the condensed trie
|
||||||
left := NoChildrenHash
|
left := NoChildrenHash
|
||||||
if len(childHashes) > 0 {
|
if len(childHashes) > 0 {
|
||||||
left = computeMerkleRoot(childHashes)
|
left = node.ComputeMerkleRoot(childHashes)
|
||||||
}
|
}
|
||||||
right := NoClaimsHash
|
right := NoClaimsHash
|
||||||
if claimsHash != nil {
|
if v.claimsHash != nil {
|
||||||
b.Write(claimsHash[:]) // for Has Value, nolint : errchk
|
b.Write(v.claimsHash[:]) // for Has Value, nolint : errchk
|
||||||
right = claimsHash
|
right = v.claimsHash
|
||||||
}
|
}
|
||||||
|
|
||||||
h := hashMerkleBranches(left, right)
|
h := node.HashMerkleBranches(left, right)
|
||||||
v.merkleHash = h
|
v.merkleHash = h
|
||||||
t.repo.Set(append(prefix, h[:]...), b.Bytes())
|
t.repo.Set(append(prefix, h[:]...), b.Bytes())
|
||||||
} else if len(childHashes) == 1 {
|
} else if len(childHashes) == 1 {
|
||||||
|
@ -249,11 +244,11 @@ func (t *MerkleTrie) merkleAllClaims(prefix []byte, v *vertex) *chainhash.Hash {
|
||||||
return v.merkleHash
|
return v.merkleHash
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *MerkleTrie) Close() error {
|
func (t *PersistentTrie) Close() error {
|
||||||
return t.repo.Close()
|
return t.repo.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *MerkleTrie) Dump(s string, allClaims bool) {
|
func (t *PersistentTrie) Dump(s string, allClaims bool) {
|
||||||
v := t.root
|
v := t.root
|
||||||
|
|
||||||
for i := 0; i < len(s); i++ {
|
for i := 0; i < len(s); i++ {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
"github.com/btcsuite/btcd/claimtrie/node"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -15,10 +16,10 @@ func TestName(t *testing.T) {
|
||||||
target, _ := chainhash.NewHashFromStr("e9ffb584c62449f157c8be88257bd1eebb2d8ef824f5c86b43c4f8fd9e800d6a")
|
target, _ := chainhash.NewHashFromStr("e9ffb584c62449f157c8be88257bd1eebb2d8ef824f5c86b43c4f8fd9e800d6a")
|
||||||
|
|
||||||
data := []*chainhash.Hash{EmptyTrieHash}
|
data := []*chainhash.Hash{EmptyTrieHash}
|
||||||
root := computeMerkleRoot(data)
|
root := node.ComputeMerkleRoot(data)
|
||||||
r.True(EmptyTrieHash.IsEqual(root))
|
r.True(EmptyTrieHash.IsEqual(root))
|
||||||
|
|
||||||
data = append(data, NoChildrenHash, NoClaimsHash)
|
data = append(data, NoChildrenHash, NoClaimsHash)
|
||||||
root = computeMerkleRoot(data)
|
root = node.ComputeMerkleRoot(data)
|
||||||
r.True(target.IsEqual(root))
|
r.True(target.IsEqual(root))
|
||||||
}
|
}
|
||||||
|
|
139
claimtrie/merkletrie/ramtrie.go
Normal file
139
claimtrie/merkletrie/ramtrie.go
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
package merkletrie
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
"github.com/btcsuite/btcd/claimtrie/node"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MerkleTrie interface {
|
||||||
|
SetRoot(h *chainhash.Hash, names [][]byte)
|
||||||
|
Update(name []byte, restoreChildren bool)
|
||||||
|
MerkleHash() *chainhash.Hash
|
||||||
|
MerkleHashAllClaims() *chainhash.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
type RamTrie struct {
|
||||||
|
collapsedTrie
|
||||||
|
store ValueStore
|
||||||
|
bufs *sync.Pool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRamTrie(s ValueStore) *RamTrie {
|
||||||
|
return &RamTrie{
|
||||||
|
store: s,
|
||||||
|
bufs: &sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return new(bytes.Buffer)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
collapsedTrie: collapsedTrie{Root: &collapsedVertex{}},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *RamTrie) SetRoot(h *chainhash.Hash, names [][]byte) {
|
||||||
|
if rt.Root.merkleHash.IsEqual(h) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// if names is nil then we need to query all names
|
||||||
|
if names == nil {
|
||||||
|
fmt.Printf("Building the entire claim trie in RAM...\n")
|
||||||
|
// TODO: should technically clear the old trie first
|
||||||
|
rt.store.IterateNames(func(name []byte) bool {
|
||||||
|
rt.Update(name, false)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
for _, name := range names {
|
||||||
|
rt.Update(name, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *RamTrie) Update(name []byte, _ bool) {
|
||||||
|
h := rt.store.Hash(name)
|
||||||
|
if h == nil {
|
||||||
|
rt.Erase(name)
|
||||||
|
} else {
|
||||||
|
_, n := rt.InsertOrFind(name)
|
||||||
|
n.claimHash = h
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *RamTrie) MerkleHash() *chainhash.Hash {
|
||||||
|
if h := rt.merkleHash(rt.Root); h == nil {
|
||||||
|
return EmptyTrieHash
|
||||||
|
}
|
||||||
|
return rt.Root.merkleHash
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *RamTrie) merkleHash(v *collapsedVertex) *chainhash.Hash {
|
||||||
|
if v.merkleHash != nil {
|
||||||
|
return v.merkleHash
|
||||||
|
}
|
||||||
|
|
||||||
|
b := rt.bufs.Get().(*bytes.Buffer)
|
||||||
|
defer rt.bufs.Put(b)
|
||||||
|
b.Reset()
|
||||||
|
|
||||||
|
for _, ch := range v.children {
|
||||||
|
h := rt.merkleHash(ch) // h is a pointer; don't destroy its data
|
||||||
|
b.WriteByte(ch.key[0]) // nolint : errchk
|
||||||
|
b.Write(rt.completeHash(h, ch.key)) // nolint : errchk
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.claimHash != nil {
|
||||||
|
b.Write(v.claimHash[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.Len() > 0 {
|
||||||
|
h := chainhash.DoubleHashH(b.Bytes())
|
||||||
|
v.merkleHash = &h
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.merkleHash
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *RamTrie) completeHash(h *chainhash.Hash, childKey KeyType) []byte {
|
||||||
|
var data [chainhash.HashSize + 1]byte
|
||||||
|
copy(data[1:], h[:])
|
||||||
|
for i := len(childKey) - 1; i > 0; i-- {
|
||||||
|
data[0] = childKey[i]
|
||||||
|
copy(data[1:], chainhash.DoubleHashB(data[:]))
|
||||||
|
}
|
||||||
|
return data[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *RamTrie) MerkleHashAllClaims() *chainhash.Hash {
|
||||||
|
return rt.merkleHashAllClaims(rt.Root)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *RamTrie) merkleHashAllClaims(v *collapsedVertex) *chainhash.Hash {
|
||||||
|
if v.merkleHash != nil {
|
||||||
|
return v.merkleHash
|
||||||
|
}
|
||||||
|
|
||||||
|
childHashes := make([]*chainhash.Hash, 0, len(v.children))
|
||||||
|
for _, ch := range v.children {
|
||||||
|
h := rt.merkleHashAllClaims(ch)
|
||||||
|
childHashes = append(childHashes, h)
|
||||||
|
}
|
||||||
|
|
||||||
|
claimHash := NoClaimsHash
|
||||||
|
if v.claimHash != nil {
|
||||||
|
claimHash = v.claimHash
|
||||||
|
} else if len(childHashes) == 0 {
|
||||||
|
return v.merkleHash
|
||||||
|
}
|
||||||
|
|
||||||
|
childHash := NoChildrenHash
|
||||||
|
if len(childHashes) > 0 {
|
||||||
|
childHash = node.ComputeMerkleRoot(childHashes)
|
||||||
|
}
|
||||||
|
|
||||||
|
v.merkleHash = node.HashMerkleBranches(childHash, claimHash)
|
||||||
|
return v.merkleHash
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Repo defines APIs for MerkleTrie to access persistence layer.
|
// Repo defines APIs for PersistentTrie to access persistence layer.
|
||||||
type Repo interface {
|
type Repo interface {
|
||||||
Get(key []byte) ([]byte, io.Closer, error)
|
Get(key []byte) ([]byte, io.Closer, error)
|
||||||
Set(key, value []byte) error
|
Set(key, value []byte) error
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package merkletrie
|
package node
|
||||||
|
|
||||||
import "github.com/btcsuite/btcd/chaincfg/chainhash"
|
import "github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
|
||||||
func hashMerkleBranches(left *chainhash.Hash, right *chainhash.Hash) *chainhash.Hash {
|
func HashMerkleBranches(left *chainhash.Hash, right *chainhash.Hash) *chainhash.Hash {
|
||||||
// Concatenate the left and right nodes.
|
// Concatenate the left and right nodes.
|
||||||
var hash [chainhash.HashSize * 2]byte
|
var hash [chainhash.HashSize * 2]byte
|
||||||
copy(hash[:chainhash.HashSize], left[:])
|
copy(hash[:chainhash.HashSize], left[:])
|
||||||
|
@ -12,7 +12,7 @@ func hashMerkleBranches(left *chainhash.Hash, right *chainhash.Hash) *chainhash.
|
||||||
return &newHash
|
return &newHash
|
||||||
}
|
}
|
||||||
|
|
||||||
func computeMerkleRoot(hashes []*chainhash.Hash) *chainhash.Hash {
|
func ComputeMerkleRoot(hashes []*chainhash.Hash) *chainhash.Hash {
|
||||||
if len(hashes) <= 0 {
|
if len(hashes) <= 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ func computeMerkleRoot(hashes []*chainhash.Hash) *chainhash.Hash {
|
||||||
hashes = append(hashes, hashes[len(hashes)-1])
|
hashes = append(hashes, hashes[len(hashes)-1])
|
||||||
}
|
}
|
||||||
for i := 0; i < len(hashes); i += 2 { // TODO: parallelize this loop (or use a lib that does it)
|
for i := 0; i < len(hashes); i += 2 { // TODO: parallelize this loop (or use a lib that does it)
|
||||||
hashes[i>>1] = hashMerkleBranches(hashes[i], hashes[i+1])
|
hashes[i>>1] = HashMerkleBranches(hashes[i], hashes[i+1])
|
||||||
}
|
}
|
||||||
hashes = hashes[:len(hashes)>>1]
|
hashes = hashes[:len(hashes)>>1]
|
||||||
}
|
}
|
|
@ -21,7 +21,6 @@ type Manager interface {
|
||||||
Node(name []byte) (*Node, error)
|
Node(name []byte) (*Node, error)
|
||||||
NextUpdateHeightOfNode(name []byte) ([]byte, int32)
|
NextUpdateHeightOfNode(name []byte) ([]byte, int32)
|
||||||
IterateNames(predicate func(name []byte) bool)
|
IterateNames(predicate func(name []byte) bool)
|
||||||
ClaimHashes(name []byte) []*chainhash.Hash
|
|
||||||
Hash(name []byte) *chainhash.Hash
|
Hash(name []byte) *chainhash.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,7 +294,7 @@ func (nm *BaseManager) IterateNames(predicate func(name []byte) bool) {
|
||||||
nm.repo.IterateAll(predicate)
|
nm.repo.IterateAll(predicate)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (nm *BaseManager) ClaimHashes(name []byte) []*chainhash.Hash {
|
func (nm *BaseManager) claimHashes(name []byte) *chainhash.Hash {
|
||||||
|
|
||||||
n, err := nm.Node(name)
|
n, err := nm.Node(name)
|
||||||
if err != nil || n == nil {
|
if err != nil || n == nil {
|
||||||
|
@ -308,11 +307,18 @@ func (nm *BaseManager) ClaimHashes(name []byte) []*chainhash.Hash {
|
||||||
claimHashes = append(claimHashes, calculateNodeHash(c.OutPoint, n.TakenOverAt))
|
claimHashes = append(claimHashes, calculateNodeHash(c.OutPoint, n.TakenOverAt))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return claimHashes
|
if len(claimHashes) > 0 {
|
||||||
|
return ComputeMerkleRoot(claimHashes)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (nm *BaseManager) Hash(name []byte) *chainhash.Hash {
|
func (nm *BaseManager) Hash(name []byte) *chainhash.Hash {
|
||||||
|
|
||||||
|
if nm.height >= param.AllClaimsInMerkleForkHeight {
|
||||||
|
return nm.claimHashes(name)
|
||||||
|
}
|
||||||
|
|
||||||
n, err := nm.Node(name)
|
n, err := nm.Node(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
|
|
Loading…
Add table
Reference in a new issue