Skip to content

Commit 8a5340a

Browse files
committed
[lbry] refactored parallel hash computation location, other hash code
formatting
1 parent f418b2b commit 8a5340a

File tree

8 files changed

+178
-161
lines changed

8 files changed

+178
-161
lines changed

claimtrie/claimtrie.go

Lines changed: 86 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ package claimtrie
22

33
import (
44
"bytes"
5+
"fmt"
56
"path/filepath"
7+
"runtime"
68
"sort"
9+
"sync"
710

811
"github.com/pkg/errors"
912

@@ -80,12 +83,13 @@ func New(cfg config.Config) (*ClaimTrie, error) {
8083
if err != nil {
8184
return nil, errors.Wrap(err, "creating node base manager")
8285
}
83-
nodeManager := node.NewNormalizingManager(baseManager)
86+
normalizingManager := node.NewNormalizingManager(baseManager)
87+
nodeManager := &node.HashV2Manager{Manager: normalizingManager}
8488
cleanups = append(cleanups, nodeManager.Close)
8589

8690
var trie merkletrie.MerkleTrie
8791
if cfg.RamTrie {
88-
trie = merkletrie.NewRamTrie(nodeManager)
92+
trie = merkletrie.NewRamTrie()
8993
} else {
9094

9195
// Initialize repository for MerkleTrie. The cleanup is delegated to MerkleTrie.
@@ -95,7 +99,7 @@ func New(cfg config.Config) (*ClaimTrie, error) {
9599
return nil, errors.Wrap(err, "creating trie repo")
96100
}
97101

98-
persistentTrie := merkletrie.NewPersistentTrie(nodeManager, trieRepo)
102+
persistentTrie := merkletrie.NewPersistentTrie(trieRepo)
99103
cleanups = append(cleanups, persistentTrie.Close)
100104
trie = persistentTrie
101105
}
@@ -129,8 +133,11 @@ func New(cfg config.Config) (*ClaimTrie, error) {
129133
ct.Close()
130134
return nil, errors.Wrap(err, "increment height to")
131135
}
132-
// TODO: pass in the interrupt signal here:
133-
trie.SetRoot(hash, nil) // keep this after IncrementHeightTo
136+
err = trie.SetRoot(hash) // keep this after IncrementHeightTo
137+
if err == merkletrie.ErrFullRebuildRequired {
138+
// TODO: pass in the interrupt signal here:
139+
ct.runFullTrieRebuild(nil)
140+
}
134141

135142
if !ct.MerkleHash().IsEqual(hash) {
136143
ct.Close()
@@ -235,7 +242,7 @@ func (ct *ClaimTrie) AppendBlock() error {
235242
names = append(names, expirations...)
236243
names = removeDuplicates(names)
237244

238-
nhns := ct.nodeManager.MakeNameHashNext(names, false)
245+
nhns := ct.makeNameHashNext(names, false)
239246
for nhn := range nhns {
240247

241248
ct.merkleTrie.Update(nhn.Name, nhn.Hash, true)
@@ -260,26 +267,19 @@ func (ct *ClaimTrie) AppendBlock() error {
260267
ct.blockRepo.Set(ct.height, h)
261268

262269
if hitFork {
263-
ct.merkleTrie.SetRoot(h, names) // for clearing the memory entirely
270+
err = ct.merkleTrie.SetRoot(h) // for clearing the memory entirely
264271
}
265272

266-
return nil
273+
return errors.Wrap(err, "merkle trie clear memory")
267274
}
268275

269276
func (ct *ClaimTrie) updateTrieForHashForkIfNecessary() bool {
270277
if ct.height != param.ActiveParams.AllClaimsInMerkleForkHeight {
271278
return false
272279
}
273280

274-
node.LogOnce("Marking all trie nodes as dirty for the hash fork...")
275-
276-
// invalidate all names because we have to recompute the hash on everything
277-
pairs := ct.nodeManager.MakeNameHashNext(nil, true)
278-
for pair := range pairs {
279-
ct.merkleTrie.Update(pair.Name, pair.Hash, false)
280-
}
281-
282-
node.LogOnce("Done. Now recomputing all hashes...")
281+
node.LogOnce(fmt.Sprintf("Rebuilding all trie nodes for the hash fork at %d...", ct.height))
282+
ct.runFullTrieRebuild(nil)
283283
return true
284284
}
285285

@@ -322,14 +322,32 @@ func (ct *ClaimTrie) ResetHeight(height int32) error {
322322
if passedHashFork {
323323
names = nil // force them to reconsider all names
324324
}
325-
ct.merkleTrie.SetRoot(hash, names)
325+
err = ct.merkleTrie.SetRoot(hash)
326+
if err == merkletrie.ErrFullRebuildRequired {
327+
ct.runFullTrieRebuild(names)
328+
}
326329

327330
if !ct.MerkleHash().IsEqual(hash) {
328331
return errors.Errorf("unable to restore the hash at height %d", height)
329332
}
330333
return nil
331334
}
332335

336+
func (ct *ClaimTrie) runFullTrieRebuild(names [][]byte) {
337+
var nhns chan NameHashNext
338+
if names == nil {
339+
node.LogOnce("Building the entire claim trie in RAM...")
340+
341+
nhns = ct.makeNameHashNext(nil, true)
342+
} else {
343+
nhns = ct.makeNameHashNext(names, false)
344+
}
345+
346+
for nhn := range nhns {
347+
ct.merkleTrie.Update(nhn.Name, nhn.Hash, false)
348+
}
349+
}
350+
333351
// MerkleHash returns the Merkle Hash of the claimTrie.
334352
func (ct *ClaimTrie) MerkleHash() *chainhash.Hash {
335353
if ct.height >= param.ActiveParams.AllClaimsInMerkleForkHeight {
@@ -392,3 +410,53 @@ func (ct *ClaimTrie) FlushToDisk() {
392410
node.Warn("During blockRepo flush: " + err.Error())
393411
}
394412
}
413+
414+
type NameHashNext struct {
415+
Name []byte
416+
Hash *chainhash.Hash
417+
Next int32
418+
}
419+
420+
func (ct *ClaimTrie) makeNameHashNext(names [][]byte, all bool) chan NameHashNext {
421+
inputs := make(chan []byte, 512)
422+
outputs := make(chan NameHashNext, 512)
423+
424+
var wg sync.WaitGroup
425+
computeHash := func() {
426+
for name := range inputs {
427+
hash, next := ct.nodeManager.Hash(name)
428+
outputs <- NameHashNext{name, hash, next}
429+
}
430+
wg.Done()
431+
}
432+
433+
threads := int(0.8 * float32(runtime.NumCPU()))
434+
if threads < 1 {
435+
threads = 1
436+
}
437+
for threads >= 0 {
438+
threads--
439+
wg.Add(1)
440+
go computeHash()
441+
}
442+
go func() {
443+
if all {
444+
ct.nodeManager.IterateNames(func(name []byte) bool {
445+
clone := make([]byte, len(name))
446+
copy(clone, name) // iteration name buffer is reused on future loops
447+
inputs <- clone
448+
return true
449+
})
450+
} else {
451+
for _, name := range names {
452+
inputs <- name
453+
}
454+
}
455+
close(inputs)
456+
}()
457+
go func() {
458+
wg.Wait()
459+
close(outputs)
460+
}()
461+
return outputs
462+
}

claimtrie/claimtrie_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,8 +254,8 @@ func TestRebuild(t *testing.T) {
254254
r.NotNil(m)
255255
r.NotEqual(*merkletrie.EmptyTrieHash, *m)
256256

257-
ct.merkleTrie = merkletrie.NewRamTrie(ct.nodeManager)
258-
ct.merkleTrie.SetRoot(m, nil)
257+
ct.merkleTrie = merkletrie.NewRamTrie()
258+
ct.runFullTrieRebuild(nil)
259259

260260
m2 := ct.MerkleHash()
261261
r.NotNil(m2)

claimtrie/cmd/cmd/merkletrie.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,10 @@ func NewTrieNameCommand() *cobra.Command {
6666
return errors.Wrapf(err, "open merkle trie repo")
6767
}
6868

69-
trie := merkletrie.NewPersistentTrie(nil, trieRepo)
69+
trie := merkletrie.NewPersistentTrie(trieRepo)
7070
defer trie.Close()
7171

72-
trie.SetRoot(&hash, nil)
72+
trie.SetRoot(&hash)
7373

7474
if len(name) > 1 {
7575
trie.Dump(name)

claimtrie/merkletrie/merkletrie.go

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,19 @@ var (
2020
NoClaimsHash = &chainhash.Hash{3}
2121
)
2222

23-
// ValueStore enables PersistentTrie to query node values from different implementations.
24-
type ValueStore interface {
25-
MakeNameHashNext(names [][]byte, all bool) chan node.NameHashNext
26-
}
27-
2823
// PersistentTrie implements a 256-way prefix tree.
2924
type PersistentTrie struct {
30-
store ValueStore
31-
repo Repo
25+
repo Repo
3226

3327
root *vertex
3428
bufs *sync.Pool
3529
}
3630

3731
// NewPersistentTrie returns a PersistentTrie.
38-
func NewPersistentTrie(store ValueStore, repo Repo) *PersistentTrie {
32+
func NewPersistentTrie(repo Repo) *PersistentTrie {
3933

4034
tr := &PersistentTrie{
41-
store: store,
42-
repo: repo,
35+
repo: repo,
4336
bufs: &sync.Pool{
4437
New: func() interface{} {
4538
return new(bytes.Buffer)
@@ -52,9 +45,10 @@ func NewPersistentTrie(store ValueStore, repo Repo) *PersistentTrie {
5245
}
5346

5447
// SetRoot drops all resolved nodes in the PersistentTrie, and set the Root with specified hash.
55-
func (t *PersistentTrie) SetRoot(h *chainhash.Hash, names [][]byte) {
48+
func (t *PersistentTrie) SetRoot(h *chainhash.Hash) error {
5649
t.root = newVertex(h)
5750
runtime.GC()
51+
return nil
5852
}
5953

6054
// Update updates the nodes along the path to the key.

claimtrie/merkletrie/ramtrie.go

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package merkletrie
22

33
import (
44
"bytes"
5+
"errors"
56
"runtime"
67
"sync"
78

@@ -10,7 +11,7 @@ import (
1011
)
1112

1213
type MerkleTrie interface {
13-
SetRoot(h *chainhash.Hash, names [][]byte)
14+
SetRoot(h *chainhash.Hash) error
1415
Update(name []byte, h *chainhash.Hash, restoreChildren bool)
1516
MerkleHash() *chainhash.Hash
1617
MerkleHashAllClaims() *chainhash.Hash
@@ -19,13 +20,11 @@ type MerkleTrie interface {
1920

2021
type RamTrie struct {
2122
collapsedTrie
22-
store ValueStore
23-
bufs *sync.Pool
23+
bufs *sync.Pool
2424
}
2525

26-
func NewRamTrie(s ValueStore) *RamTrie {
26+
func NewRamTrie() *RamTrie {
2727
return &RamTrie{
28-
store: s,
2928
bufs: &sync.Pool{
3029
New: func() interface{} {
3130
return new(bytes.Buffer)
@@ -35,30 +34,22 @@ func NewRamTrie(s ValueStore) *RamTrie {
3534
}
3635
}
3736

38-
func (rt *RamTrie) SetRoot(h *chainhash.Hash, names [][]byte) {
37+
var ErrFullRebuildRequired = errors.New("a full rebuild is required")
38+
39+
func (rt *RamTrie) SetRoot(h *chainhash.Hash) error {
3940
if rt.Root.merkleHash.IsEqual(h) {
4041
runtime.GC()
41-
return
42+
return nil
4243
}
4344

44-
var nhns chan node.NameHashNext
45-
if names == nil {
46-
node.LogOnce("Building the entire claim trie in RAM...") // could put this in claimtrie.go
47-
48-
// should technically clear the old trie first:
49-
if rt.Nodes > 1 {
50-
rt.Root = &collapsedVertex{key: make(KeyType, 0)}
51-
rt.Nodes = 1
52-
runtime.GC()
53-
}
54-
nhns = rt.store.MakeNameHashNext(nil, true)
55-
} else {
56-
nhns = rt.store.MakeNameHashNext(names, false)
45+
// should technically clear the old trie first:
46+
if rt.Nodes > 1 {
47+
rt.Root = &collapsedVertex{key: make(KeyType, 0)}
48+
rt.Nodes = 1
49+
runtime.GC()
5750
}
5851

59-
for nhn := range nhns {
60-
rt.Update(nhn.Name, nhn.Hash, false)
61-
}
52+
return ErrFullRebuildRequired
6253
}
6354

6455
func (rt *RamTrie) Update(name []byte, h *chainhash.Hash, _ bool) {

claimtrie/node/hashfork_manager.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package node
2+
3+
import (
4+
"github.com/btcsuite/btcd/chaincfg/chainhash"
5+
"github.com/btcsuite/btcd/claimtrie/param"
6+
)
7+
8+
type HashV2Manager struct {
9+
Manager
10+
}
11+
12+
func (nm *HashV2Manager) computeClaimHashes(name []byte) (*chainhash.Hash, int32) {
13+
14+
n, err := nm.NodeAt(nm.Height(), name)
15+
if err != nil || n == nil {
16+
return nil, 0
17+
}
18+
19+
n.SortClaimsByBid()
20+
claimHashes := make([]*chainhash.Hash, 0, len(n.Claims))
21+
for _, c := range n.Claims {
22+
if c.Status == Activated { // TODO: unit test this line
23+
claimHashes = append(claimHashes, calculateNodeHash(c.OutPoint, n.TakenOverAt))
24+
}
25+
}
26+
if len(claimHashes) > 0 {
27+
return ComputeMerkleRoot(claimHashes), n.NextUpdate()
28+
}
29+
return nil, n.NextUpdate()
30+
}
31+
32+
func (nm *HashV2Manager) Hash(name []byte) (*chainhash.Hash, int32) {
33+
34+
if nm.Height() >= param.ActiveParams.AllClaimsInMerkleForkHeight {
35+
return nm.computeClaimHashes(name)
36+
}
37+
38+
return nm.Manager.Hash(name)
39+
}

claimtrie/node/hashfunc.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
package node
22

3-
import "github.com/btcsuite/btcd/chaincfg/chainhash"
3+
import (
4+
"crypto/sha256"
5+
"encoding/binary"
6+
"strconv"
7+
8+
"github.com/btcsuite/btcd/chaincfg/chainhash"
9+
"github.com/btcsuite/btcd/wire"
10+
)
411

512
func HashMerkleBranches(left *chainhash.Hash, right *chainhash.Hash) *chainhash.Hash {
613
// Concatenate the left and right nodes.
@@ -27,3 +34,24 @@ func ComputeMerkleRoot(hashes []*chainhash.Hash) *chainhash.Hash {
2734
}
2835
return hashes[0]
2936
}
37+
38+
func calculateNodeHash(op wire.OutPoint, takeover int32) *chainhash.Hash {
39+
40+
txHash := chainhash.DoubleHashH(op.Hash[:])
41+
42+
nOut := []byte(strconv.Itoa(int(op.Index)))
43+
nOutHash := chainhash.DoubleHashH(nOut)
44+
45+
buf := make([]byte, 8)
46+
binary.BigEndian.PutUint64(buf, uint64(takeover))
47+
heightHash := chainhash.DoubleHashH(buf)
48+
49+
h := make([]byte, 0, sha256.Size*3)
50+
h = append(h, txHash[:]...)
51+
h = append(h, nOutHash[:]...)
52+
h = append(h, heightHash[:]...)
53+
54+
hh := chainhash.DoubleHashH(h)
55+
56+
return &hh
57+
}

0 commit comments

Comments
 (0)