github.com/decred/politeia@v1.4.0/politeiad/backendv2/tstorebe/tstore/freeze.go (about) 1 // Copyright (c) 2022 The Decred developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 package tstore 6 7 import ( 8 "errors" 9 10 backend "github.com/decred/politeia/politeiad/backendv2" 11 "github.com/google/trillian" 12 ) 13 14 // freezeTreeCheck checks if any trillian trees meet the requirements to be 15 // frozen. If they do, their status is updated in trillian to frozen. 16 // 17 // A frozen trillian tree can no longer be appended to. The trillian_log_signer 18 // will no longer poll the MySQL database for updates to a tree once it has 19 // been marked as frozen. This reduces the load on the server and helps prevent 20 // the CPUs from spinning. 21 // 22 // A record is marked as frozen when it can no longer be updated, such as when 23 // a record status is set to archived. The trillian tree, however, cannot be 24 // frozen until the record is frozen AND a final dcr timestamp has been added 25 // to tree. This means that we cannot simply freeze the tree at the same time 26 // that the record is frozen since it will still need to be timestamped one 27 // last time. 28 func (t *Tstore) freezeTreeCheck() error { 29 log.Infof("Checking if any trillian trees can be frozen") 30 31 trees, err := t.tlog.TreesAll() 32 if err != nil { 33 return err 34 } 35 36 active := make([]*trillian.Tree, 0, len(trees)) 37 for _, v := range trees { 38 if v.TreeState == trillian.TreeState_ACTIVE { 39 active = append(active, v) 40 } 41 } 42 43 log.Infof("%v/%v active trillian trees found", len(active), len(trees)) 44 45 var frozen int 46 for _, tree := range active { 47 freeze, err := t.treeShouldBeFrozen(tree) 48 if err != nil { 49 log.Errorf("treeShouldBeFrozen %v: %v", tree.TreeId, err) 50 continue 51 } 52 if !freeze { 53 // Tree shouldn't be frozen. Nothing else to do. 54 continue 55 } 56 _, err = t.tlog.TreeFreeze(tree.TreeId) 57 if err != nil { 58 return err 59 } 60 61 log.Infof("Tree frozen %v %x", tree.TreeId, tokenFromTreeID(tree.TreeId)) 62 63 frozen++ 64 } 65 66 log.Infof("%v trees were frozen; %v active trees remaining", 67 frozen, len(active)-frozen) 68 69 return nil 70 } 71 72 // treeShouldBeFrozen returns whether a trillian tree meets the requirements to 73 // have it's status updated from ACTIVE to FROZEN. The requirments are that the 74 // tree is currently active, the record saved to the tree has been frozen, and 75 // a final dcr timestamp has been added to the tree. 76 func (t *Tstore) treeShouldBeFrozen(tree *trillian.Tree) (bool, error) { 77 if tree.TreeState != trillian.TreeState_ACTIVE { 78 return false, nil 79 } 80 leaves, err := t.tlog.LeavesAll(tree.TreeId) 81 if err != nil { 82 return false, err 83 } 84 if len(leaves) == 0 { 85 return false, nil 86 } 87 r, err := t.recordIndexLatest(leaves) 88 switch { 89 case errors.Is(err, backend.ErrRecordNotFound): 90 // A record index doesn't exist on this tree 91 return false, nil 92 case err != nil: 93 return false, err 94 } 95 if !r.Frozen { 96 // The record has not been frozen yet 97 return false, nil 98 } 99 // The record has been frozen. Check for a final 100 // timestamp leaf. 101 lastLeaf := leaves[len(leaves)-1] 102 d, err := extraDataDecode(lastLeaf.ExtraData) 103 if err != nil { 104 return false, err 105 } 106 if d.Desc != dataDescriptorAnchor { 107 // The tree still needs a final timestamp. 108 return false, nil 109 } 110 // The record has been frozen and a final timestamp 111 // has been added to the tree. The tree can now be 112 // frozen. 113 return true, nil 114 }