github.com/tenywen/fabric@v1.0.0-beta.0.20170620030522-a5b1ed380643/orderer/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/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 // Iterator returns an Iterator, as specified by a cb.SeekInfo message, and its 112 // starting block number 113 func (jl *jsonLedger) Iterator(startPosition *ab.SeekPosition) (ledger.Iterator, uint64) { 114 switch start := startPosition.Type.(type) { 115 case *ab.SeekPosition_Oldest: 116 return &cursor{jl: jl, blockNumber: 0}, 0 117 case *ab.SeekPosition_Newest: 118 high := jl.height - 1 119 return &cursor{jl: jl, blockNumber: high}, high 120 case *ab.SeekPosition_Specified: 121 if start.Specified.Number > jl.height { 122 return &ledger.NotFoundErrorIterator{}, 0 123 } 124 return &cursor{jl: jl, blockNumber: start.Specified.Number}, start.Specified.Number 125 default: 126 return &ledger.NotFoundErrorIterator{}, 0 127 } 128 } 129 130 // Height returns the number of blocks on the ledger 131 func (jl *jsonLedger) Height() uint64 { 132 return jl.height 133 } 134 135 // Append appends a new block to the ledger 136 func (jl *jsonLedger) Append(block *cb.Block) error { 137 if block.Header.Number != jl.height { 138 return fmt.Errorf("Block number should have been %d but was %d", jl.height, block.Header.Number) 139 } 140 141 if !bytes.Equal(block.Header.PreviousHash, jl.lastHash) { 142 return fmt.Errorf("Block should have had previous hash of %x but was %x", jl.lastHash, block.Header.PreviousHash) 143 } 144 145 jl.writeBlock(block) 146 jl.lastHash = block.Header.Hash() 147 jl.height++ 148 close(jl.signal) 149 jl.signal = make(chan struct{}) 150 return nil 151 } 152 153 // writeBlock commits a block to disk 154 func (jl *jsonLedger) writeBlock(block *cb.Block) { 155 name := jl.blockFilename(block.Header.Number) 156 157 fileLock.Lock() 158 defer fileLock.Unlock() 159 160 file, err := os.Create(name) 161 if err != nil { 162 panic(err) 163 } 164 defer file.Close() 165 166 err = jl.marshaler.Marshal(file, block) 167 logger.Debugf("Wrote block %d", block.Header.Number) 168 if err != nil { 169 logger.Panic(err) 170 } 171 } 172 173 // blockFilename returns the fully qualified path to where a block 174 // of a given number should be stored on disk 175 func (jl *jsonLedger) blockFilename(number uint64) string { 176 return filepath.Join(jl.directory, fmt.Sprintf(blockFileFormatString, number)) 177 }