github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/orderer/ledger/file/fileledger.go (about) 1 /* 2 Copyright IBM Corp. 2016 All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package fileledger 18 19 import ( 20 "bytes" 21 "fmt" 22 "io/ioutil" 23 "os" 24 "sync" 25 26 ordererledger "github.com/hyperledger/fabric/orderer/ledger" 27 cb "github.com/hyperledger/fabric/protos/common" 28 ab "github.com/hyperledger/fabric/protos/orderer" 29 "github.com/op/go-logging" 30 31 "github.com/golang/protobuf/jsonpb" 32 ) 33 34 var logger = logging.MustGetLogger("ordererledger/fileledger") 35 var closedChan chan struct{} 36 37 func init() { 38 closedChan = make(chan struct{}) 39 close(closedChan) 40 } 41 42 const ( 43 blockFileFormatString = "block_%020d.json" 44 chainDirectoryFormatString = "chain_%s" 45 ) 46 47 type cursor struct { 48 fl *fileLedger 49 blockNumber uint64 50 } 51 52 type fileLedger struct { 53 directory string 54 fqFormatString string 55 height uint64 56 signal chan struct{} 57 lastHash []byte 58 marshaler *jsonpb.Marshaler 59 } 60 61 type fileLedgerFactory struct { 62 directory string 63 ledgers map[string]ordererledger.ReadWriter 64 mutex sync.Mutex 65 } 66 67 // New creates a new fileledger Factory and the ordering system chain specified by the systemGenesis block (if it does not already exist) 68 func New(directory string) ordererledger.Factory { 69 70 logger.Debugf("Initializing fileLedger at '%s'", directory) 71 if err := os.MkdirAll(directory, 0700); err != nil { 72 logger.Fatalf("Could not create directory %s: %s", directory, err) 73 } 74 75 flf := &fileLedgerFactory{ 76 directory: directory, 77 ledgers: make(map[string]ordererledger.ReadWriter), 78 } 79 80 infos, err := ioutil.ReadDir(flf.directory) 81 if err != nil { 82 logger.Panicf("Error reading from directory %s while initializing fileledger: %s", flf.directory, err) 83 } 84 85 for _, info := range infos { 86 if !info.IsDir() { 87 continue 88 } 89 var chainID string 90 _, err := fmt.Sscanf(info.Name(), chainDirectoryFormatString, &chainID) 91 if err != nil { 92 continue 93 } 94 fl, err := flf.GetOrCreate(chainID) 95 if err != nil { 96 logger.Warningf("Failed to initialize chain from %s:", err) 97 continue 98 } 99 flf.ledgers[chainID] = fl 100 } 101 102 return flf 103 } 104 105 func (flf *fileLedgerFactory) ChainIDs() []string { 106 flf.mutex.Lock() 107 defer flf.mutex.Unlock() 108 ids := make([]string, len(flf.ledgers)) 109 110 i := 0 111 for key := range flf.ledgers { 112 ids[i] = key 113 i++ 114 } 115 116 return ids 117 } 118 119 func (flf *fileLedgerFactory) GetOrCreate(chainID string) (ordererledger.ReadWriter, error) { 120 flf.mutex.Lock() 121 defer flf.mutex.Unlock() 122 123 key := chainID 124 125 l, ok := flf.ledgers[key] 126 if ok { 127 return l, nil 128 } 129 130 directory := fmt.Sprintf("%s/"+chainDirectoryFormatString, flf.directory, chainID) 131 132 logger.Debugf("Initializing chain at '%s'", directory) 133 134 if err := os.MkdirAll(directory, 0700); err != nil { 135 return nil, err 136 } 137 138 ch := newChain(directory) 139 flf.ledgers[key] = ch 140 return ch, nil 141 } 142 143 // newChain creates a new chain backed by a file ledger 144 func newChain(directory string) ordererledger.ReadWriter { 145 fl := &fileLedger{ 146 directory: directory, 147 fqFormatString: directory + "/" + blockFileFormatString, 148 signal: make(chan struct{}), 149 marshaler: &jsonpb.Marshaler{Indent: " "}, 150 } 151 fl.initializeBlockHeight() 152 logger.Debugf("Initialized to block height %d with hash %x", fl.height-1, fl.lastHash) 153 return fl 154 } 155 156 // initializeBlockHeight verifies all blocks exist between 0 and the block height, and populates the lastHash 157 func (fl *fileLedger) initializeBlockHeight() { 158 infos, err := ioutil.ReadDir(fl.directory) 159 if err != nil { 160 panic(err) 161 } 162 nextNumber := uint64(0) 163 for _, info := range infos { 164 if info.IsDir() { 165 continue 166 } 167 var number uint64 168 _, err := fmt.Sscanf(info.Name(), blockFileFormatString, &number) 169 if err != nil { 170 continue 171 } 172 if number != nextNumber { 173 panic(fmt.Errorf("Missing block %d in the chain", nextNumber)) 174 } 175 nextNumber++ 176 } 177 fl.height = nextNumber 178 if fl.height == 0 { 179 return 180 } 181 block, found := fl.readBlock(fl.height - 1) 182 if !found { 183 panic(fmt.Errorf("Block %d was in directory listing but error reading", fl.height-1)) 184 } 185 if block == nil { 186 panic(fmt.Errorf("Error reading block %d", fl.height-1)) 187 } 188 fl.lastHash = block.Header.Hash() 189 } 190 191 // blockFilename returns the fully qualified path to where a block of a given number should be stored on disk 192 func (fl *fileLedger) blockFilename(number uint64) string { 193 return fmt.Sprintf(fl.fqFormatString, number) 194 } 195 196 // writeBlock commits a block to disk 197 func (fl *fileLedger) writeBlock(block *cb.Block) { 198 file, err := os.Create(fl.blockFilename(block.Header.Number)) 199 if err != nil { 200 panic(err) 201 } 202 defer file.Close() 203 err = fl.marshaler.Marshal(file, block) 204 logger.Debugf("Wrote block %d", block.Header.Number) 205 if err != nil { 206 panic(err) 207 } 208 209 } 210 211 // readBlock returns the block or nil, and whether the block was found or not, (nil,true) generally indicates an irrecoverable problem 212 func (fl *fileLedger) readBlock(number uint64) (*cb.Block, bool) { 213 file, err := os.Open(fl.blockFilename(number)) 214 if err == nil { 215 defer file.Close() 216 block := &cb.Block{} 217 err = jsonpb.Unmarshal(file, block) 218 if err != nil { 219 return nil, true 220 } 221 logger.Debugf("Read block %d", block.Header.Number) 222 return block, true 223 } 224 return nil, false 225 } 226 227 // Height returns the highest block number in the chain, plus one 228 func (fl *fileLedger) Height() uint64 { 229 return fl.height 230 } 231 232 // Append appends a new block to the ledger 233 func (fl *fileLedger) Append(block *cb.Block) error { 234 if block.Header.Number != fl.height { 235 return fmt.Errorf("Block number should have been %d but was %d", fl.height, block.Header.Number) 236 } 237 238 if !bytes.Equal(block.Header.PreviousHash, fl.lastHash) { 239 return fmt.Errorf("Block should have had previous hash of %x but was %x", fl.lastHash, block.Header.PreviousHash) 240 } 241 242 fl.writeBlock(block) 243 fl.lastHash = block.Header.Hash() 244 fl.height++ 245 close(fl.signal) 246 fl.signal = make(chan struct{}) 247 return nil 248 } 249 250 // Iterator implements the ordererledger.Reader definition 251 func (fl *fileLedger) Iterator(startPosition *ab.SeekPosition) (ordererledger.Iterator, uint64) { 252 switch start := startPosition.Type.(type) { 253 case *ab.SeekPosition_Oldest: 254 return &cursor{fl: fl, blockNumber: 0}, 0 255 case *ab.SeekPosition_Newest: 256 high := fl.height - 1 257 return &cursor{fl: fl, blockNumber: high}, high 258 case *ab.SeekPosition_Specified: 259 if start.Specified.Number > fl.height { 260 return &ordererledger.NotFoundErrorIterator{}, 0 261 } 262 return &cursor{fl: fl, blockNumber: start.Specified.Number}, start.Specified.Number 263 } 264 265 // This line should be unreachable, but the compiler requires it 266 return &ordererledger.NotFoundErrorIterator{}, 0 267 } 268 269 // Next blocks until there is a new block available, or returns an error if the next block is no longer retrievable 270 func (cu *cursor) Next() (*cb.Block, cb.Status) { 271 // This only loops once, as signal reading indicates the new block has been written 272 for { 273 block, found := cu.fl.readBlock(cu.blockNumber) 274 if found { 275 if block == nil { 276 return nil, cb.Status_SERVICE_UNAVAILABLE 277 } 278 cu.blockNumber++ 279 return block, cb.Status_SUCCESS 280 } 281 <-cu.fl.signal 282 } 283 } 284 285 // ReadyChan returns a channel that will close when Next is ready to be called without blocking 286 func (cu *cursor) ReadyChan() <-chan struct{} { 287 signal := cu.fl.signal 288 if _, err := os.Stat(cu.fl.blockFilename(cu.blockNumber)); os.IsNotExist(err) { 289 return signal 290 } 291 return closedChan 292 }