From a0469820a2c3adf3b6048f64200891c3e244e16c Mon Sep 17 00:00:00 2001 From: Brannon King Date: Sat, 10 Jul 2021 15:35:52 -0400 Subject: [PATCH] refactored EffectiveAmount for performance --- claimtrie/claimtrie.go | 33 ++++++++++++------- claimtrie/cmd/cmd/ui.go | 4 +-- claimtrie/node/claim.go | 17 ---------- claimtrie/node/manager_test.go | 54 +++++++++++++++++++++++++++++++ claimtrie/node/node.go | 36 ++++++++++++++------- claimtrie/node/noderepo/pebble.go | 2 +- 6 files changed, 103 insertions(+), 43 deletions(-) diff --git a/claimtrie/claimtrie.go b/claimtrie/claimtrie.go index 1e36a184..9fa86817 100644 --- a/claimtrie/claimtrie.go +++ b/claimtrie/claimtrie.go @@ -23,6 +23,8 @@ import ( "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" + "runtime" + "sort" ) // ClaimTrie implements a Merkle Trie supporting linear history of commits. @@ -110,18 +112,6 @@ func New(cfg config.Config) (*ClaimTrie, error) { return nil, fmt.Errorf("load blocks: %w", err) } - if previousHeight > 0 { - hash, err := blockRepo.Get(previousHeight) - if err != nil { - return nil, fmt.Errorf("get hash: %w", err) - } - _, err = nodeManager.IncrementHeightTo(previousHeight) - if err != nil { - return nil, fmt.Errorf("node manager init: %w", err) - } - trie.SetRoot(hash, nil) // keep this after IncrementHeightTo - } - ct := &ClaimTrie{ blockRepo: blockRepo, temporalRepo: temporalRepo, @@ -149,6 +139,25 @@ func New(cfg config.Config) (*ClaimTrie, error) { } ct.cleanups = cleanups + if previousHeight > 0 { + hash, err := blockRepo.Get(previousHeight) + if err != nil { + ct.Close() // TODO: the cleanups aren't run when we exit with an err above here (but should be) + return nil, fmt.Errorf("get hash: %w", err) + } + _, err = nodeManager.IncrementHeightTo(previousHeight) + if err != nil { + ct.Close() + return nil, fmt.Errorf("node manager init: %w", err) + } + trie.SetRoot(hash, nil) // keep this after IncrementHeightTo + + if !ct.MerkleHash().IsEqual(hash) { + ct.Close() + return nil, fmt.Errorf("unable to restore the claim hash to %s at height %d", hash.String(), previousHeight) + } + } + return ct, nil } diff --git a/claimtrie/cmd/cmd/ui.go b/claimtrie/cmd/cmd/ui.go index 4fc8d42a..6bb78c02 100644 --- a/claimtrie/cmd/cmd/ui.go +++ b/claimtrie/cmd/cmd/ui.go @@ -41,8 +41,8 @@ func showClaim(c *node.Claim, n *node.Node) { mark = "*" } - fmt.Printf("%s C ID: %s, TXO: %s\n %5d/%-5d, Status: %9s, Amount: %15d, Effective Amount: %15d\n", - mark, c.ClaimID, c.OutPoint, c.AcceptedAt, c.ActiveAt, status[c.Status], c.Amount, c.EffectiveAmount(n.Supports)) + fmt.Printf("%s C ID: %s, TXO: %s\n %5d/%-5d, Status: %9s, Amount: %15d, Support Amount: %15d\n", + mark, c.ClaimID, c.OutPoint, c.AcceptedAt, c.ActiveAt, status[c.Status], c.Amount, n.SupportSums[c.ClaimID]) } func showSupport(c *node.Claim) { diff --git a/claimtrie/node/claim.go b/claimtrie/node/claim.go index e0a42ab5..eabdbd57 100644 --- a/claimtrie/node/claim.go +++ b/claimtrie/node/claim.go @@ -101,23 +101,6 @@ func (c *Claim) setStatus(status Status) *Claim { return c } -func (c *Claim) EffectiveAmount(supports ClaimList) int64 { - - if c.Status != Activated { - return 0 - } - - amt := c.Amount - - for _, s := range supports { - if s.Status == Activated && s.ClaimID == c.ClaimID { // TODO: this comparison is hit a lot; byte comparison instead of hex would be faster - amt += s.Amount - } - } - - return amt -} - func (c *Claim) ExpireAt() int32 { if c.AcceptedAt+param.OriginalClaimExpirationTime > param.ExtendedClaimExpirationForkHeight { diff --git a/claimtrie/node/manager_test.go b/claimtrie/node/manager_test.go index 0a1b557c..f12f9dac 100644 --- a/claimtrie/node/manager_test.go +++ b/claimtrie/node/manager_test.go @@ -16,6 +16,7 @@ var ( out1 = NewOutPointFromString("0000000000000000000000000000000000000000000000000000000000000000:1") out2 = NewOutPointFromString("0000000000000000000000000000000000000000000000000000000000000000:2") out3 = NewOutPointFromString("0100000000000000000000000000000000000000000000000000000000000000:1") + out4 = NewOutPointFromString("0100000000000000000000000000000000000000000000000000000000000000:2") name1 = []byte("name1") name2 = []byte("name2") ) @@ -51,6 +52,7 @@ func TestSimpleAddClaim(t *testing.T) { m, err := NewBaseManager(repo) r.NoError(err) + defer m.Close() _, err = m.IncrementHeightTo(10) r.NoError(err) @@ -90,6 +92,58 @@ func TestSimpleAddClaim(t *testing.T) { r.Nil(n2) } +func TestSupportAmounts(t *testing.T) { + + r := require.New(t) + + param.SetNetwork(wire.TestNet, "") + repo, err := noderepo.NewPebble(t.TempDir()) + r.NoError(err) + + m, err := NewBaseManager(repo) + r.NoError(err) + defer m.Close() + + _, err = m.IncrementHeightTo(10) + r.NoError(err) + + chg := change.New(change.AddClaim).SetName(name1).SetOutPoint(out1.String()).SetHeight(11).SetAmount(3) + chg.ClaimID = NewClaimID(*out1).String() + err = m.AppendChange(chg) + r.NoError(err) + + chg = change.New(change.AddClaim).SetName(name1).SetOutPoint(out2.String()).SetHeight(11).SetAmount(4) + chg.ClaimID = NewClaimID(*out2).String() + err = m.AppendChange(chg) + r.NoError(err) + + _, err = m.IncrementHeightTo(11) + r.NoError(err) + + chg = change.New(change.AddSupport).SetName(name1).SetOutPoint(out3.String()).SetHeight(12).SetAmount(2) + chg.ClaimID = NewClaimID(*out1).String() + err = m.AppendChange(chg) + r.NoError(err) + + chg = change.New(change.AddSupport).SetName(name1).SetOutPoint(out4.String()).SetHeight(12).SetAmount(2) + chg.ClaimID = NewClaimID(*out2).String() + err = m.AppendChange(chg) + r.NoError(err) + + chg = change.New(change.SpendSupport).SetName(name1).SetOutPoint(out4.String()).SetHeight(12).SetAmount(2) + chg.ClaimID = NewClaimID(*out2).String() + err = m.AppendChange(chg) + r.NoError(err) + + _, err = m.IncrementHeightTo(20) + r.NoError(err) + + n1, err := m.Node(name1) + r.NoError(err) + r.Equal(2, len(n1.Claims)) + r.Equal(int64(5), n1.BestClaim.Amount+n1.SupportSums[n1.BestClaim.ClaimID]) +} + func TestNodeSort(t *testing.T) { r := require.New(t) diff --git a/claimtrie/node/node.go b/claimtrie/node/node.go index a7dd7c1c..66dfa0db 100644 --- a/claimtrie/node/node.go +++ b/claimtrie/node/node.go @@ -17,11 +17,12 @@ type Node struct { TakenOverAt int32 // The height at when the current BestClaim took over. Claims ClaimList // List of all Claims. Supports ClaimList // List of all Supports, including orphaned ones. + SupportSums map[string]int64 } // New returns a new node. func New() *Node { - return &Node{} + return &Node{SupportSums: map[string]int64{}} } func (n *Node) ApplyChange(chg change.Change, delay int32) error { @@ -94,6 +95,12 @@ func (n *Node) ApplyChange(chg change.Change, delay int32) error { case change.SpendSupport: s := n.Supports.find(byOut(*out)) if s != nil { + if s.Status == Activated { + n.SupportSums[s.ClaimID] -= s.Amount + } + // TODO: we could do without this Deactivated flag if we set expiration instead + // That would eliminate the above Sum update. + // We would also need to track the update situation, though, but that could be done locally. s.setStatus(Deactivated) } else { fmt.Printf("Spending support but missing existing support with TXO %s\n "+ @@ -151,12 +158,15 @@ func (n *Node) updateTakeoverHeight(height int32, name []byte, refindBest bool) func (n *Node) handleExpiredAndActivated(height int32) int { changes := 0 - update := func(items ClaimList) ClaimList { + update := func(items ClaimList, sums map[string]int64) ClaimList { for i := 0; i < len(items); i++ { c := items[i] if c.Status == Accepted && c.ActiveAt <= height && c.VisibleAt <= height { c.setStatus(Activated) changes++ + if sums != nil { + sums[c.ClaimID] += c.Amount + } } if c.ExpireAt() <= height || c.Status == Deactivated { if i < len(items)-1 { @@ -165,12 +175,15 @@ func (n *Node) handleExpiredAndActivated(height int32) int { } items = items[:len(items)-1] changes++ + if sums != nil && c.Status != Deactivated { + sums[c.ClaimID] -= c.Amount + } } } return items } - n.Claims = update(n.Claims) - n.Supports = update(n.Supports) + n.Claims = update(n.Claims, nil) + n.Supports = update(n.Supports, n.SupportSums) return changes } @@ -234,9 +247,9 @@ func (n Node) findBestClaim() *Claim { continue } - candidateAmount := candidate.EffectiveAmount(n.Supports) - if bestAmount <= 0 { // trying to reduce calls to EffectiveAmount - bestAmount = best.EffectiveAmount(n.Supports) + candidateAmount := candidate.Amount + n.SupportSums[candidate.ClaimID] + if bestAmount <= 0 { + bestAmount = best.Amount + n.SupportSums[best.ClaimID] } switch { @@ -263,7 +276,7 @@ func (n *Node) activateAllClaims(height int32) int { count := 0 for _, c := range n.Claims { if c.Status == Accepted && c.ActiveAt > height && c.VisibleAt <= height { - c.setActiveAt(height) // don't necessary need to change this number + c.setActiveAt(height) // don't necessarily need to change this number? c.setStatus(Activated) count++ } @@ -271,9 +284,10 @@ func (n *Node) activateAllClaims(height int32) int { for _, s := range n.Supports { if s.Status == Accepted && s.ActiveAt > height && s.VisibleAt <= height { - s.setActiveAt(height) // don't necessary need to change this number + s.setActiveAt(height) // don't necessarily need to change this number? s.setStatus(Activated) count++ + n.SupportSums[s.ClaimID] += s.Amount } } return count @@ -283,8 +297,8 @@ func (n *Node) SortClaims() { // purposefully sorting by descent sort.Slice(n.Claims, func(j, i int) bool { - iAmount := n.Claims[i].EffectiveAmount(n.Supports) - jAmount := n.Claims[j].EffectiveAmount(n.Supports) + iAmount := n.Claims[i].Amount + n.SupportSums[n.Claims[i].ClaimID] + jAmount := n.Claims[j].Amount + n.SupportSums[n.Claims[j].ClaimID] switch { case iAmount < jAmount: return true diff --git a/claimtrie/node/noderepo/pebble.go b/claimtrie/node/noderepo/pebble.go index b0e4bab0..3da46cd9 100644 --- a/claimtrie/node/noderepo/pebble.go +++ b/claimtrie/node/noderepo/pebble.go @@ -15,7 +15,7 @@ type Pebble struct { func NewPebble(path string) (*Pebble, error) { - db, err := pebble.Open(path, &pebble.Options{Cache: pebble.NewCache(128 << 20), BytesPerSync: 16 << 20}) + db, err := pebble.Open(path, &pebble.Options{Cache: pebble.NewCache(256 << 20), BytesPerSync: 16 << 20}) if err != nil { return nil, fmt.Errorf("pebble open %s, %w", path, err) }