gitlab.com/SiaPrime/SiaPrime@v1.4.1/modules/consensus/changelog.go (about) 1 package consensus 2 3 // changelog.go implements a persistent changelog in the consenus database 4 // tracking all of the atomic changes to the consensus set. The primary use of 5 // the changelog is for subscribers that have persistence - instead of 6 // subscribing from the very beginning and receiving all changes from genesis 7 // each time the daemon starts up, the subscribers can start from the most 8 // recent change that they are familiar with. 9 // 10 // The changelog is set up as a singley linked list where each change points 11 // forward to the next change. In bolt, the key is a hash of the changeEntry 12 // and the value is a struct containing the changeEntry and the key of the next 13 // changeEntry. The empty hash key leads to the 'changeTail', which contains 14 // the id of the most recent changeEntry. 15 // 16 // Initialization only needs to worry about creating the blank change entry, 17 // the genesis block will call 'append' later on during initialization. 18 19 import ( 20 bolt "github.com/coreos/bbolt" 21 "gitlab.com/SiaPrime/SiaPrime/build" 22 "gitlab.com/SiaPrime/SiaPrime/crypto" 23 "gitlab.com/SiaPrime/SiaPrime/encoding" 24 "gitlab.com/SiaPrime/SiaPrime/modules" 25 "gitlab.com/SiaPrime/SiaPrime/types" 26 ) 27 28 var ( 29 // ChangeLog contains a list of atomic changes that have happened to the 30 // consensus set so that subscribers can subscribe from the most recent 31 // change they have seen. 32 ChangeLog = []byte("ChangeLog") 33 34 // ChangeLogTailID is a key that points to the id of the current changelog 35 // tail. 36 ChangeLogTailID = []byte("ChangeLogTailID") 37 ) 38 39 type ( 40 // changeEntry records a single atomic change to the consensus set. 41 changeEntry struct { 42 RevertedBlocks []types.BlockID 43 AppliedBlocks []types.BlockID 44 } 45 46 // changeNode contains a change entry and a pointer to the next change 47 // entry, and is the object that gets stored in the database. 48 changeNode struct { 49 Entry changeEntry 50 Next modules.ConsensusChangeID 51 } 52 ) 53 54 // appendChangeLog adds a new change entry to the change log. 55 func appendChangeLog(tx *bolt.Tx, ce changeEntry) error { 56 // Insert the change entry. 57 cl := tx.Bucket(ChangeLog) 58 ceid := ce.ID() 59 cn := changeNode{Entry: ce, Next: modules.ConsensusChangeID{}} 60 err := cl.Put(ceid[:], encoding.Marshal(cn)) 61 if err != nil { 62 return err 63 } 64 65 // Update the tail node to point to the new change entry as the next entry. 66 var tailID modules.ConsensusChangeID 67 copy(tailID[:], cl.Get(ChangeLogTailID)) 68 if tailID != (modules.ConsensusChangeID{}) { 69 // Get the old tail node. 70 var tailCN changeNode 71 tailCNBytes := cl.Get(tailID[:]) 72 err = encoding.Unmarshal(tailCNBytes, &tailCN) 73 if err != nil { 74 return err 75 } 76 77 // Point the 'next' of the old tail node to the new tail node and 78 // insert. 79 tailCN.Next = ceid 80 err = cl.Put(tailID[:], encoding.Marshal(tailCN)) 81 if err != nil { 82 return err 83 } 84 } 85 86 // Update the tail id. 87 err = cl.Put(ChangeLogTailID, ceid[:]) 88 if err != nil { 89 return err 90 } 91 return nil 92 } 93 94 // getEntry returns the change entry with a given id, using a bool to indicate 95 // existence. 96 func getEntry(tx *bolt.Tx, id modules.ConsensusChangeID) (ce changeEntry, exists bool) { 97 var cn changeNode 98 cl := tx.Bucket(ChangeLog) 99 changeNodeBytes := cl.Get(id[:]) 100 if changeNodeBytes == nil { 101 return changeEntry{}, false 102 } 103 err := encoding.Unmarshal(changeNodeBytes, &cn) 104 if build.DEBUG && err != nil { 105 panic(err) 106 } 107 return cn.Entry, true 108 } 109 110 // ID returns the id of a change entry. 111 func (ce *changeEntry) ID() modules.ConsensusChangeID { 112 return modules.ConsensusChangeID(crypto.HashObject(ce)) 113 } 114 115 // NextEntry returns the entry after the current entry. 116 func (ce *changeEntry) NextEntry(tx *bolt.Tx) (nextEntry changeEntry, exists bool) { 117 // Get the change node associated with the provided change entry. 118 ceid := ce.ID() 119 var cn changeNode 120 cl := tx.Bucket(ChangeLog) 121 changeNodeBytes := cl.Get(ceid[:]) 122 err := encoding.Unmarshal(changeNodeBytes, &cn) 123 if err != nil { 124 build.Critical(err) 125 } 126 127 return getEntry(tx, cn.Next) 128 } 129 130 // createChangeLog assumes that no change log exists and creates a new one. 131 func (cs *ConsensusSet) createChangeLog(tx *bolt.Tx) error { 132 // Create the changelog bucket. 133 cl, err := tx.CreateBucket(ChangeLog) 134 if err != nil { 135 return err 136 } 137 138 // Add the genesis block as the first entry of the change log. 139 ge := cs.genesisEntry() 140 geid := ge.ID() 141 cn := changeNode{ 142 Entry: ge, 143 Next: modules.ConsensusChangeID{}, 144 } 145 err = cl.Put(geid[:], encoding.Marshal(cn)) 146 if err != nil { 147 return err 148 } 149 err = cl.Put(ChangeLogTailID, geid[:]) 150 if err != nil { 151 return err 152 } 153 return nil 154 } 155 156 // genesisEntry returns the id of the genesis block log entry. 157 func (cs *ConsensusSet) genesisEntry() changeEntry { 158 return changeEntry{ 159 AppliedBlocks: []types.BlockID{cs.blockRoot.Block.ID()}, 160 } 161 }