From d46bedf5efe8c13ed554ea71f285d269408dd1ec Mon Sep 17 00:00:00 2001 From: Brannon King Date: Mon, 19 Jul 2021 11:11:26 -0400 Subject: [PATCH] fixed emulation of old delay calculation --- claimtrie/change/change.go | 2 ++ claimtrie/node/manager.go | 53 ++++++++++++++++++++++++++++++++++++-- claimtrie/node/node.go | 3 ++- 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/claimtrie/change/change.go b/claimtrie/change/change.go index 9a029271..cfce9982 100644 --- a/claimtrie/change/change.go +++ b/claimtrie/change/change.go @@ -22,6 +22,8 @@ type Change struct { ActiveHeight int32 // for normalization fork VisibleHeight int32 + + SpentChildren map[string]bool } func New(typ ChangeType) Change { diff --git a/claimtrie/node/manager.go b/claimtrie/node/manager.go index ac60ec14..1d271bc7 100644 --- a/claimtrie/node/manager.go +++ b/claimtrie/node/manager.go @@ -1,6 +1,7 @@ package node import ( + "bytes" "crypto/sha256" "encoding/binary" "fmt" @@ -121,12 +122,54 @@ func (nm *BaseManager) AppendChange(chg change.Change) error { return nil } +func collectChildNames(changes []change.Change) { + // we need to determine which children (names that start with the same name) go with which change + // if we have the names in order then we can avoid iterating through all names in the change list + // and we can possibly reuse the previous list. + // eh. optimize it some other day + + // what would happen in the old code: + // spending a claim (which happens before every update) could remove a node from the cached trie + // in which case we would fall back on the data from the previous block (where it obviously wasn't spent). + // It would only delete the node if it had no children, but have even some rare situations + // Where all of the children happen to be deleted first. That's what we must detect here. + + // Algorithm: + // For each non-spend change + // Loop through all the spends before you and add them to your child list if they are your child + + for i := range changes { + t := changes[i].Type + if t == change.SpendClaim || t == change.SpendSupport { + continue + } + a := changes[i].Name + sc := map[string]bool{} + for j := 0; j < i; j++ { + t = changes[j].Type + if t != change.SpendClaim { + continue + } + b := changes[j].Name + if len(b) >= len(a) && bytes.Equal(a, b[:len(a)]) { + sc[string(b)] = true + } + } + changes[i].SpentChildren = sc + } +} + func (nm *BaseManager) IncrementHeightTo(height int32) ([][]byte, error) { if height <= nm.height { panic("invalid height") } + if height >= param.MaxRemovalWorkaroundHeight { + // not technically needed until block 884430, but to be true to the arbitrary rollback length... + collectChildNames(nm.changes) + } + names := make([][]byte, 0, len(nm.changes)) for i := range nm.changes { names = append(names, nm.changes[i].Name) @@ -226,7 +269,7 @@ func (nm *BaseManager) aWorkaroundIsNeeded(n *Node, chg change.Change) bool { // auto it = nodesToAddOrUpdate.find(name); // nodesToAddOrUpdate is the working changes, base is previous block // auto answer = (it || (it = base->find(name))) && !it->empty() ? nNextHeight - it->nHeightOfLastTakeover : 0; - needed := hasZeroActiveClaims(n) && nm.hasChildren(chg.Name, chg.Height, 2) + needed := hasZeroActiveClaims(n) && nm.hasChildren(chg.Name, chg.Height, chg.SpentChildren, 2) if chg.Height <= 933294 { w := isInDelayPart2(chg) if w { @@ -288,8 +331,11 @@ func (nm *BaseManager) Close() error { return nil } -func (nm *BaseManager) hasChildren(name []byte, height int32, required int) bool { +func (nm *BaseManager) hasChildren(name []byte, height int32, spentChildren map[string]bool, required int) bool { c := map[byte]bool{} + if spentChildren == nil { + spentChildren = map[string]bool{} + } nm.repo.IterateChildren(name, func(changes []change.Change) bool { // if the key is unseen, generate a node for it to height @@ -300,6 +346,9 @@ func (nm *BaseManager) hasChildren(name []byte, height int32, required int) bool if c[changes[0].Name[len(name)]] { // assuming all names here are longer than starter name return true // we already checked a similar name } + if spentChildren[string(changes[0].Name)] { + return true // children that are spent in the same block cannot count as active children + } n, _ := nm.newNodeFromChanges(changes, height) if n != nil && n.HasActiveBestClaim() { c[changes[0].Name[len(name)]] = true diff --git a/claimtrie/node/node.go b/claimtrie/node/node.go index a650ee13..c8859995 100644 --- a/claimtrie/node/node.go +++ b/claimtrie/node/node.go @@ -75,7 +75,8 @@ func (n *Node) ApplyChange(chg change.Change, delay int32) error { // Keep its ID, which was generated from the spent claim. // And update the rest of properties. c.setOutPoint(*out).SetAmt(chg.Amount).SetValue(chg.Value) - c.setStatus(Accepted) // it was Deactivated in the spend + c.setStatus(Accepted) // it was Deactivated in the spend (but we only activate at the end of the block) + // that's because the old code would put all insertions into the "queue" that was processed at block's end // It's a bug, but the old code would update these. // That forces this to be newer, which may in an unintentional takeover if there's an older one.