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