github.com/NebulousLabs/Sia@v1.3.7/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 "github.com/NebulousLabs/Sia/build" 21 "github.com/NebulousLabs/Sia/crypto" 22 "github.com/NebulousLabs/Sia/encoding" 23 "github.com/NebulousLabs/Sia/modules" 24 "github.com/NebulousLabs/Sia/types" 25 26 "github.com/coreos/bbolt" 27 ) 28 29 var ( 30 // ChangeLog contains a list of atomic changes that have happened to the 31 // consensus set so that subscribers can subscribe from the most recent 32 // change they have seen. 33 ChangeLog = []byte("ChangeLog") 34 35 // ChangeLogTailID is a key that points to the id of the current changelog 36 // tail. 37 ChangeLogTailID = []byte("ChangeLogTailID") 38 ) 39 40 type ( 41 // changeEntry records a single atomic change to the consensus set. 42 changeEntry struct { 43 RevertedBlocks []types.BlockID 44 AppliedBlocks []types.BlockID 45 } 46 47 // changeNode contains a change entry and a pointer to the next change 48 // entry, and is the object that gets stored in the database. 49 changeNode struct { 50 Entry changeEntry 51 Next modules.ConsensusChangeID 52 } 53 ) 54 55 // appendChangeLog adds a new change entry to the change log. 56 func appendChangeLog(tx *bolt.Tx, ce changeEntry) error { 57 // Insert the change entry. 58 cl := tx.Bucket(ChangeLog) 59 ceid := ce.ID() 60 cn := changeNode{Entry: ce, Next: modules.ConsensusChangeID{}} 61 err := cl.Put(ceid[:], encoding.Marshal(cn)) 62 if err != nil { 63 return err 64 } 65 66 // Update the tail node to point to the new change entry as the next entry. 67 var tailID modules.ConsensusChangeID 68 copy(tailID[:], cl.Get(ChangeLogTailID)) 69 if tailID != (modules.ConsensusChangeID{}) { 70 // Get the old tail node. 71 var tailCN changeNode 72 tailCNBytes := cl.Get(tailID[:]) 73 err = encoding.Unmarshal(tailCNBytes, &tailCN) 74 if err != nil { 75 return err 76 } 77 78 // Point the 'next' of the old tail node to the new tail node and 79 // insert. 80 tailCN.Next = ceid 81 err = cl.Put(tailID[:], encoding.Marshal(tailCN)) 82 if err != nil { 83 return err 84 } 85 } 86 87 // Update the tail id. 88 err = cl.Put(ChangeLogTailID, ceid[:]) 89 if err != nil { 90 return err 91 } 92 return nil 93 } 94 95 // getEntry returns the change entry with a given id, using a bool to indicate 96 // existence. 97 func getEntry(tx *bolt.Tx, id modules.ConsensusChangeID) (ce changeEntry, exists bool) { 98 var cn changeNode 99 cl := tx.Bucket(ChangeLog) 100 changeNodeBytes := cl.Get(id[:]) 101 if changeNodeBytes == nil { 102 return changeEntry{}, false 103 } 104 err := encoding.Unmarshal(changeNodeBytes, &cn) 105 if build.DEBUG && err != nil { 106 panic(err) 107 } 108 return cn.Entry, true 109 } 110 111 // ID returns the id of a change entry. 112 func (ce *changeEntry) ID() modules.ConsensusChangeID { 113 return modules.ConsensusChangeID(crypto.HashObject(ce)) 114 } 115 116 // NextEntry returns the entry after the current entry. 117 func (ce *changeEntry) NextEntry(tx *bolt.Tx) (nextEntry changeEntry, exists bool) { 118 // Get the change node associated with the provided change entry. 119 ceid := ce.ID() 120 var cn changeNode 121 cl := tx.Bucket(ChangeLog) 122 changeNodeBytes := cl.Get(ceid[:]) 123 err := encoding.Unmarshal(changeNodeBytes, &cn) 124 if err != nil { 125 build.Critical(err) 126 } 127 128 return getEntry(tx, cn.Next) 129 } 130 131 // createChangeLog assumes that no change log exists and creates a new one. 132 func (cs *ConsensusSet) createChangeLog(tx *bolt.Tx) error { 133 // Create the changelog bucket. 134 cl, err := tx.CreateBucket(ChangeLog) 135 if err != nil { 136 return err 137 } 138 139 // Add the genesis block as the first entry of the change log. 140 ge := cs.genesisEntry() 141 geid := ge.ID() 142 cn := changeNode{ 143 Entry: ge, 144 Next: modules.ConsensusChangeID{}, 145 } 146 err = cl.Put(geid[:], encoding.Marshal(cn)) 147 if err != nil { 148 return err 149 } 150 err = cl.Put(ChangeLogTailID, geid[:]) 151 if err != nil { 152 return err 153 } 154 return nil 155 } 156 157 // genesisEntry returns the id of the genesis block log entry. 158 func (cs *ConsensusSet) genesisEntry() changeEntry { 159 return changeEntry{ 160 AppliedBlocks: []types.BlockID{cs.blockRoot.Block.ID()}, 161 } 162 }