factory.go 3.48 KB
Newer Older
1
/*
2
Copyright IBM Corp. All Rights Reserved.
3

4
SPDX-License-Identifier: Apache-2.0
5
6
7
8
9
10
11
12
13
14
15
16
*/

package jsonledger

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"

	"github.com/golang/protobuf/jsonpb"
17
	"github.com/hyperledger/fabric/common/ledger/blockledger"
18
	"github.com/pkg/errors"
19
20
21
22
)

type jsonLedgerFactory struct {
	directory string
23
	ledgers   map[string]blockledger.ReadWriter
24
25
26
27
	mutex     sync.Mutex
}

// GetOrCreate gets an existing ledger (if it exists) or creates it if it does not
28
func (jlf *jsonLedgerFactory) GetOrCreate(chainID string) (blockledger.ReadWriter, error) {
29
30
31
32
33
34
35
36
37
38
39
40
	jlf.mutex.Lock()
	defer jlf.mutex.Unlock()

	key := chainID

	l, ok := jlf.ledgers[key]
	if ok {
		return l, nil
	}

	directory := filepath.Join(jlf.directory, fmt.Sprintf(chainDirectoryFormatString, chainID))

41
	logger.Debugf("Initializing chain %s at: %s", chainID, directory)
42
43

	if err := os.MkdirAll(directory, 0700); err != nil {
44
45
		logger.Errorf("Error initializing channel %s: %s", chainID, err)
		return nil, errors.Wrapf(err, "error initializing channel %s", chainID)
46
47
48
49
50
51
52
53
	}

	ch := newChain(directory)
	jlf.ledgers[key] = ch
	return ch, nil
}

// newChain creates a new chain backed by a JSON ledger
54
func newChain(directory string) blockledger.ReadWriter {
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
	jl := &jsonLedger{
		directory: directory,
		signal:    make(chan struct{}),
		marshaler: &jsonpb.Marshaler{Indent: "  "},
	}
	jl.initializeBlockHeight()
	logger.Debugf("Initialized to block height %d with hash %x", jl.height-1, jl.lastHash)
	return jl
}

// initializeBlockHeight verifies that all blocks exist between 0 and the block
// height, and populates the lastHash
func (jl *jsonLedger) initializeBlockHeight() {
	infos, err := ioutil.ReadDir(jl.directory)
	if err != nil {
		logger.Panic(err)
	}
	nextNumber := uint64(0)
	for _, info := range infos {
		if info.IsDir() {
			continue
		}
		var number uint64
		_, err := fmt.Sscanf(info.Name(), blockFileFormatString, &number)
		if err != nil {
			continue
		}
		if number != nextNumber {
			logger.Panicf("Missing block %d in the chain", nextNumber)
		}
		nextNumber++
	}
	jl.height = nextNumber
	if jl.height == 0 {
		return
	}
	block, found := jl.readBlock(jl.height - 1)
	if !found {
		logger.Panicf("Block %d was in directory listing but error reading", jl.height-1)
	}
	if block == nil {
		logger.Panicf("Error reading block %d", jl.height-1)
	}
	jl.lastHash = block.Header.Hash()
}

// ChainIDs returns the chain IDs the factory is aware of
func (jlf *jsonLedgerFactory) ChainIDs() []string {
	jlf.mutex.Lock()
	defer jlf.mutex.Unlock()
	ids := make([]string, len(jlf.ledgers))

	i := 0
	for key := range jlf.ledgers {
		ids[i] = key
		i++
	}

	return ids
}

// Close is a no-op for the JSON ledger
func (jlf *jsonLedgerFactory) Close() {
	return // nothing to do
}

// New creates a new ledger factory
122
func New(directory string) blockledger.Factory {
123
124
	logger.Debugf("Initializing ledger at: %s", directory)
	if err := os.MkdirAll(directory, 0700); err != nil {
125
		logger.Panicf("Could not create directory %s: %s", directory, err)
126
127
128
129
	}

	jlf := &jsonLedgerFactory{
		directory: directory,
130
		ledgers:   make(map[string]blockledger.ReadWriter),
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
	}

	infos, err := ioutil.ReadDir(jlf.directory)
	if err != nil {
		logger.Panicf("Error reading from directory %s while initializing ledger: %s", jlf.directory, err)
	}

	for _, info := range infos {
		if !info.IsDir() {
			continue
		}
		var chainID string
		_, err := fmt.Sscanf(info.Name(), chainDirectoryFormatString, &chainID)
		if err != nil {
			continue
		}
147
		jlf.GetOrCreate(chainID)
148
149
150
151
	}

	return jlf
}