github.com/aergoio/aergo@v1.3.1/pkg/trie/trie_revert.go (about) 1 /** 2 * @file 3 * @copyright defined in aergo/LICENSE.txt 4 */ 5 6 package trie 7 8 import ( 9 "bytes" 10 "fmt" 11 ) 12 13 // Revert rewinds the state tree to a previous version 14 // All the nodes (subtree roots and values) reverted are deleted from the database. 15 func (s *Trie) Revert(toOldRoot []byte) error { 16 s.lock.RLock() 17 defer s.lock.RUnlock() 18 19 // safety precaution if reverting to a shortcut batch that might have been deleted 20 s.atomicUpdate = false // so loadChildren doesnt return a copy 21 batch, _, _, _, isShortcut, err := s.loadChildren(toOldRoot, s.TrieHeight, 0, nil) 22 if err != nil { 23 return err 24 } 25 26 //check if toOldRoot is in s.pastTries 27 canRevert := false 28 toIndex := 0 29 for i, r := range s.pastTries { 30 if bytes.Equal(r, toOldRoot) { 31 canRevert = true 32 toIndex = i 33 break 34 } 35 } 36 if !canRevert || bytes.Equal(s.Root, toOldRoot) { 37 return fmt.Errorf("The root cannot be reverted, because already latest of not in pastTries : current : %x, target : %x", s.Root, toOldRoot) 38 } 39 40 // For every node of toOldRoot, compare it to the equivalent node in other pasttries between toOldRoot and current s.Root. If a node is different, delete the one from pasttries 41 s.db.nodesToRevert = make([][]byte, 0) 42 for i := toIndex + 1; i < len(s.pastTries); i++ { 43 ch := make(chan error, 1) 44 s.maybeDeleteSubTree(toOldRoot, s.pastTries[i], s.TrieHeight, 0, nil, nil, ch) 45 err := <-ch 46 if err != nil { 47 return err 48 } 49 } 50 // NOTE The tx interface doesnt handle ErrTxnTooBig 51 txn := s.db.Store.NewTx() 52 for _, key := range s.db.nodesToRevert { 53 txn.Delete(key[:HashLength]) 54 } 55 txn.Commit() 56 57 s.pastTries = s.pastTries[:toIndex+1] 58 s.Root = toOldRoot 59 s.db.liveCache = make(map[Hash][][]byte) 60 s.db.updatedNodes = make(map[Hash][][]byte) 61 if isShortcut { 62 // If toOldRoot is a shortcut batch, it is possible that 63 // revert has deleted it if the key was ever stored at height0 64 // because in leafHash byte(0) = byte(256) 65 s.db.Store.Set(toOldRoot, s.db.serializeBatch(batch)) 66 } 67 return nil 68 } 69 70 // maybeDeleteSubTree compares the subtree nodes of 2 tries and keeps only the older one 71 func (s *Trie) maybeDeleteSubTree(original, maybeDelete []byte, height, iBatch int, batch, batch2 [][]byte, ch chan<- (error)) { 72 if height == 0 { 73 if !bytes.Equal(original, maybeDelete) && len(maybeDelete) != 0 { 74 s.maybeDeleteRevertedNode(maybeDelete, 0) 75 } 76 ch <- nil 77 return 78 } 79 if bytes.Equal(original, maybeDelete) || len(maybeDelete) == 0 { 80 ch <- nil 81 return 82 } 83 // if this point os reached, then the root of the batch is same 84 // so the batch is also same. 85 batch, iBatch, lnode, rnode, isShortcut, lerr := s.loadChildren(original, height, iBatch, batch) 86 if lerr != nil { 87 ch <- lerr 88 return 89 } 90 batch2, _, lnode2, rnode2, isShortcut2, rerr := s.loadChildren(maybeDelete, height, iBatch, batch2) 91 if rerr != nil { 92 ch <- rerr 93 return 94 } 95 96 if isShortcut != isShortcut2 { 97 if isShortcut { 98 ch1 := make(chan error, 1) 99 s.deleteSubTree(maybeDelete, height, iBatch, batch2, ch1) 100 err := <-ch1 101 if err != nil { 102 ch <- err 103 return 104 } 105 } else { 106 s.maybeDeleteRevertedNode(maybeDelete, iBatch) 107 } 108 } else { 109 if isShortcut { 110 // Delete shortcut if not equal 111 if !bytes.Equal(lnode, lnode2) || !bytes.Equal(rnode, rnode2) { 112 s.maybeDeleteRevertedNode(maybeDelete, iBatch) 113 } 114 } else { 115 // Delete subtree if not equal 116 s.maybeDeleteRevertedNode(maybeDelete, iBatch) 117 ch1 := make(chan error, 1) 118 ch2 := make(chan error, 1) 119 go s.maybeDeleteSubTree(lnode, lnode2, height-1, 2*iBatch+1, batch, batch2, ch1) 120 go s.maybeDeleteSubTree(rnode, rnode2, height-1, 2*iBatch+2, batch, batch2, ch2) 121 err1, err2 := <-ch1, <-ch2 122 if err1 != nil { 123 ch <- err1 124 return 125 } 126 if err2 != nil { 127 ch <- err2 128 return 129 } 130 } 131 } 132 ch <- nil 133 } 134 135 // deleteSubTree deletes all the nodes contained in a tree 136 func (s *Trie) deleteSubTree(root []byte, height, iBatch int, batch [][]byte, ch chan<- (error)) { 137 if len(root) == 0 || height == 0 { 138 if height == 0 { 139 s.maybeDeleteRevertedNode(root, 0) 140 } 141 ch <- nil 142 return 143 } 144 batch, iBatch, lnode, rnode, isShortcut, err := s.loadChildren(root, height, iBatch, batch) 145 if err != nil { 146 ch <- err 147 return 148 } 149 if !isShortcut { 150 ch1 := make(chan error, 1) 151 ch2 := make(chan error, 1) 152 go s.deleteSubTree(lnode, height-1, 2*iBatch+1, batch, ch1) 153 go s.deleteSubTree(rnode, height-1, 2*iBatch+2, batch, ch2) 154 lerr := <-ch1 155 rerr := <-ch2 156 157 if lerr != nil { 158 ch <- lerr 159 return 160 } 161 if rerr != nil { 162 ch <- rerr 163 return 164 } 165 } 166 s.maybeDeleteRevertedNode(root, iBatch) 167 ch <- nil 168 } 169 170 // maybeDeleteRevertedNode adds the node to updatedNodes to be reverted 171 // if it is a batch node at height%4 == 0 172 func (s *Trie) maybeDeleteRevertedNode(root []byte, iBatch int) { 173 if iBatch == 0 { 174 s.db.revertMux.Lock() 175 s.db.nodesToRevert = append(s.db.nodesToRevert, root) 176 s.db.revertMux.Unlock() 177 } 178 }