github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/orderer/ledger/ram/ramledger.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 ramledger 18 19 import ( 20 "bytes" 21 "fmt" 22 "sync" 23 24 ordererledger "github.com/hyperledger/fabric/orderer/ledger" 25 cb "github.com/hyperledger/fabric/protos/common" 26 ab "github.com/hyperledger/fabric/protos/orderer" 27 "github.com/op/go-logging" 28 ) 29 30 var logger = logging.MustGetLogger("ordererledger/ramledger") 31 32 type cursor struct { 33 list *simpleList 34 } 35 36 type simpleList struct { 37 next *simpleList 38 signal chan struct{} 39 block *cb.Block 40 } 41 42 type ramLedger struct { 43 maxSize int 44 size int 45 oldest *simpleList 46 newest *simpleList 47 } 48 49 type ramLedgerFactory struct { 50 maxSize int 51 ledgers map[string]ordererledger.ReadWriter 52 mutex sync.Mutex 53 } 54 55 // New creates a new ramledger factory and system ordering chain based on the given systemGenesis block, 56 // because there is no persistence, the new ReadWriter will have only the genesis block contained 57 func New(maxSize int) ordererledger.Factory { 58 rlf := &ramLedgerFactory{ 59 maxSize: maxSize, 60 ledgers: make(map[string]ordererledger.ReadWriter), 61 } 62 63 return rlf 64 } 65 66 func (rlf *ramLedgerFactory) GetOrCreate(chainID string) (ordererledger.ReadWriter, error) { 67 rlf.mutex.Lock() 68 defer rlf.mutex.Unlock() 69 70 key := chainID 71 72 l, ok := rlf.ledgers[key] 73 if ok { 74 return l, nil 75 } 76 77 ch := newChain(rlf.maxSize) 78 rlf.ledgers[key] = ch 79 return ch, nil 80 } 81 82 func (rlf *ramLedgerFactory) ChainIDs() []string { 83 rlf.mutex.Lock() 84 defer rlf.mutex.Unlock() 85 ids := make([]string, len(rlf.ledgers)) 86 87 i := 0 88 for key := range rlf.ledgers { 89 ids[i] = key 90 i++ 91 } 92 93 return ids 94 } 95 96 // newChain creates a new instance of the ram ledger for a chain 97 func newChain(maxSize int) ordererledger.ReadWriter { 98 preGenesis := &cb.Block{ 99 Header: &cb.BlockHeader{ 100 Number: ^uint64(0), 101 }, 102 } 103 104 rl := &ramLedger{ 105 maxSize: maxSize, 106 size: 1, 107 oldest: &simpleList{ 108 signal: make(chan struct{}), 109 block: preGenesis, 110 }, 111 } 112 rl.newest = rl.oldest 113 return rl 114 } 115 116 // Height returns the highest block number in the chain, plus one 117 func (rl *ramLedger) Height() uint64 { 118 return rl.newest.block.Header.Number + 1 119 } 120 121 // Iterator implements the ordererledger.Reader definition 122 func (rl *ramLedger) Iterator(startPosition *ab.SeekPosition) (ordererledger.Iterator, uint64) { 123 var list *simpleList 124 switch start := startPosition.Type.(type) { 125 case *ab.SeekPosition_Oldest: 126 oldest := rl.oldest 127 list = &simpleList{ 128 block: &cb.Block{Header: &cb.BlockHeader{Number: oldest.block.Header.Number - 1}}, 129 next: oldest, 130 signal: make(chan struct{}), 131 } 132 close(list.signal) 133 case *ab.SeekPosition_Newest: 134 newest := rl.newest 135 list = &simpleList{ 136 block: &cb.Block{Header: &cb.BlockHeader{Number: newest.block.Header.Number - 1}}, 137 next: newest, 138 signal: make(chan struct{}), 139 } 140 close(list.signal) 141 case *ab.SeekPosition_Specified: 142 oldest := rl.oldest 143 specified := start.Specified.Number 144 logger.Debugf("Attempting to return block %d", specified) 145 146 // Note the two +1's here is to accomodate the 'preGenesis' block of ^uint64(0) 147 if specified+1 < oldest.block.Header.Number+1 || specified > rl.newest.block.Header.Number+1 { 148 logger.Debugf("Returning error iterator because specified seek was %d with oldest %d and newest %d", specified, rl.oldest.block.Header.Number, rl.newest.block.Header.Number) 149 return &ordererledger.NotFoundErrorIterator{}, 0 150 } 151 152 if specified == oldest.block.Header.Number { 153 list = &simpleList{ 154 block: &cb.Block{Header: &cb.BlockHeader{Number: oldest.block.Header.Number - 1}}, 155 next: oldest, 156 signal: make(chan struct{}), 157 } 158 close(list.signal) 159 break 160 } 161 162 list = oldest 163 for { 164 if list.block.Header.Number == specified-1 { 165 break 166 } 167 list = list.next // No need for nil check, because of range check above 168 } 169 } 170 cursor := &cursor{list: list} 171 blockNum := list.block.Header.Number + 1 172 173 // If the cursor is for pre-genesis, skip it, the block number wraps 174 if blockNum == ^uint64(0) { 175 cursor.Next() 176 blockNum++ 177 } 178 179 return cursor, blockNum 180 } 181 182 // Next blocks until there is a new block available, or returns an error if the next block is no longer retrievable 183 func (cu *cursor) Next() (*cb.Block, cb.Status) { 184 // This only loops once, as signal reading indicates non-nil next 185 for { 186 if cu.list.next != nil { 187 cu.list = cu.list.next 188 return cu.list.block, cb.Status_SUCCESS 189 } 190 191 <-cu.list.signal 192 } 193 } 194 195 // ReadyChan returns a channel that will close when Next is ready to be called without blocking 196 func (cu *cursor) ReadyChan() <-chan struct{} { 197 return cu.list.signal 198 } 199 200 // Append appends a new block to the ledger 201 func (rl *ramLedger) Append(block *cb.Block) error { 202 if block.Header.Number != rl.newest.block.Header.Number+1 { 203 return fmt.Errorf("Block number should have been %d but was %d", rl.newest.block.Header.Number+1, block.Header.Number) 204 } 205 206 if rl.newest.block.Header.Number+1 != 0 { // Skip this check for genesis block insertion 207 if !bytes.Equal(block.Header.PreviousHash, rl.newest.block.Header.Hash()) { 208 return fmt.Errorf("Block should have had previous hash of %x but was %x", rl.newest.block.Header.Hash(), block.Header.PreviousHash) 209 } 210 } 211 212 rl.appendBlock(block) 213 return nil 214 } 215 216 func (rl *ramLedger) appendBlock(block *cb.Block) { 217 rl.newest.next = &simpleList{ 218 signal: make(chan struct{}), 219 block: block, 220 } 221 222 lastSignal := rl.newest.signal 223 logger.Debugf("Sending signal that block %d has a successor", rl.newest.block.Header.Number) 224 rl.newest = rl.newest.next 225 close(lastSignal) 226 227 rl.size++ 228 229 if rl.size > rl.maxSize { 230 logger.Debugf("RAM ledger max size about to be exceeded, removing oldest item: %d", rl.oldest.block.Header.Number) 231 rl.oldest = rl.oldest.next 232 rl.size-- 233 } 234 }