github.com/myafeier/fabric@v1.0.1-0.20170722181825-3a4b1f2bce86/orderer/common/ledger/json/impl.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 jsonledger 18 19 import ( 20 "bytes" 21 "fmt" 22 "os" 23 "path/filepath" 24 "sync" 25 26 ledger "github.com/hyperledger/fabric/orderer/common/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("orderer/jsonledger") 35 var closedChan chan struct{} 36 var fileLock sync.Mutex 37 38 func init() { 39 closedChan = make(chan struct{}) 40 close(closedChan) 41 } 42 43 const ( 44 blockFileFormatString = "block_%020d.json" 45 chainDirectoryFormatString = "chain_%s" 46 ) 47 48 type cursor struct { 49 jl *jsonLedger 50 blockNumber uint64 51 } 52 53 type jsonLedger struct { 54 directory string 55 height uint64 56 signal chan struct{} 57 lastHash []byte 58 marshaler *jsonpb.Marshaler 59 } 60 61 // readBlock returns the block or nil, and whether the block was found or not, (nil,true) generally indicates an irrecoverable problem 62 func (jl *jsonLedger) readBlock(number uint64) (*cb.Block, bool) { 63 name := jl.blockFilename(number) 64 65 // In case of ongoing write, reading the block file may result in `unexpected EOF` error. 66 // Therefore, we use file mutex here to prevent this race condition. 67 fileLock.Lock() 68 defer fileLock.Unlock() 69 70 file, err := os.Open(name) 71 if err == nil { 72 defer file.Close() 73 block := &cb.Block{} 74 err = jsonpb.Unmarshal(file, block) 75 if err != nil { 76 return nil, true 77 } 78 logger.Debugf("Read block %d", block.Header.Number) 79 return block, true 80 } 81 return nil, false 82 } 83 84 // Next blocks until there is a new block available, or returns an error if the 85 // next block is no longer retrievable 86 func (cu *cursor) Next() (*cb.Block, cb.Status) { 87 // This only loops once, as signal reading 88 // indicates the new block has been written 89 for { 90 block, found := cu.jl.readBlock(cu.blockNumber) 91 if found { 92 if block == nil { 93 return nil, cb.Status_SERVICE_UNAVAILABLE 94 } 95 cu.blockNumber++ 96 return block, cb.Status_SUCCESS 97 } 98 <-cu.jl.signal 99 } 100 } 101 102 // ReadyChan supplies a channel which will block until Next will not block 103 func (cu *cursor) ReadyChan() <-chan struct{} { 104 signal := cu.jl.signal 105 if _, err := os.Stat(cu.jl.blockFilename(cu.blockNumber)); os.IsNotExist(err) { 106 return signal 107 } 108 return closedChan 109 } 110 111 func (cu *cursor) Close() {} 112 113 // Iterator returns an Iterator, as specified by a cb.SeekInfo message, and its 114 // starting block number 115 func (jl *jsonLedger) Iterator(startPosition *ab.SeekPosition) (ledger.Iterator, uint64) { 116 switch start := startPosition.Type.(type) { 117 case *ab.SeekPosition_Oldest: 118 return &cursor{jl: jl, blockNumber: 0}, 0 119 case *ab.SeekPosition_Newest: 120 high := jl.height - 1 121 return &cursor{jl: jl, blockNumber: high}, high 122 case *ab.SeekPosition_Specified: 123 if start.Specified.Number > jl.height { 124 return &ledger.NotFoundErrorIterator{}, 0 125 } 126 return &cursor{jl: jl, blockNumber: start.Specified.Number}, start.Specified.Number 127 default: 128 return &ledger.NotFoundErrorIterator{}, 0 129 } 130 } 131 132 // Height returns the number of blocks on the ledger 133 func (jl *jsonLedger) Height() uint64 { 134 return jl.height 135 } 136 137 // Append appends a new block to the ledger 138 func (jl *jsonLedger) Append(block *cb.Block) error { 139 if block.Header.Number != jl.height { 140 return fmt.Errorf("Block number should have been %d but was %d", jl.height, block.Header.Number) 141 } 142 143 if !bytes.Equal(block.Header.PreviousHash, jl.lastHash) { 144 return fmt.Errorf("Block should have had previous hash of %x but was %x", jl.lastHash, block.Header.PreviousHash) 145 } 146 147 jl.writeBlock(block) 148 jl.lastHash = block.Header.Hash() 149 jl.height++ 150 close(jl.signal) 151 jl.signal = make(chan struct{}) 152 return nil 153 } 154 155 // writeBlock commits a block to disk 156 func (jl *jsonLedger) writeBlock(block *cb.Block) { 157 name := jl.blockFilename(block.Header.Number) 158 159 fileLock.Lock() 160 defer fileLock.Unlock() 161 162 file, err := os.Create(name) 163 if err != nil { 164 panic(err) 165 } 166 defer file.Close() 167 168 err = jl.marshaler.Marshal(file, block) 169 logger.Debugf("Wrote block %d", block.Header.Number) 170 if err != nil { 171 logger.Panic(err) 172 } 173 } 174 175 // blockFilename returns the fully qualified path to where a block 176 // of a given number should be stored on disk 177 func (jl *jsonLedger) blockFilename(number uint64) string { 178 return filepath.Join(jl.directory, fmt.Sprintf(blockFileFormatString, number)) 179 }