Skip to content

Commit 6b58537

Browse files
author
Jason Yellick
committed
[FAB-421] Add multi-chain support to rawledger
The original rawledger interface only supports a single chain. However, to support multiple chains, this interface must be extended to support multiple chains. This changeset creates a Factory interface for the rawledger which supports the method GetOrCreate which will get the ledger if it already exists, or create a new one if not. In order to preserve some backwards compatability, and to support the notion of multi-chain more generally in the future with an ordering system chain, the factories accept a genesis block to seed the ordering system chain with. Change-Id: I99844ff1b9a11f383d08c23f60d95f848632876e Signed-off-by: Jason Yellick <[email protected]>
1 parent a2b9b2e commit 6b58537

12 files changed

+269
-58
lines changed

orderer/common/deliver/deliver_test.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ func (m *mockD) Recv() (*ab.DeliverUpdate, error) {
7171

7272
func TestOldestSeek(t *testing.T) {
7373
ledgerSize := 5
74-
rl := ramledger.New(ledgerSize, genesisBlock)
74+
_, rl := ramledger.New(ledgerSize, genesisBlock)
7575
for i := 1; i < ledgerSize; i++ {
7676
rl.Append([]*cb.Envelope{&cb.Envelope{Payload: []byte(fmt.Sprintf("%d", i))}}, nil)
7777
}
@@ -103,7 +103,7 @@ func TestOldestSeek(t *testing.T) {
103103

104104
func TestNewestSeek(t *testing.T) {
105105
ledgerSize := 5
106-
rl := ramledger.New(ledgerSize, genesisBlock)
106+
_, rl := ramledger.New(ledgerSize, genesisBlock)
107107
for i := 1; i < ledgerSize; i++ {
108108
rl.Append([]*cb.Envelope{&cb.Envelope{Payload: []byte(fmt.Sprintf("%d", i))}}, nil)
109109
}
@@ -132,7 +132,7 @@ func TestNewestSeek(t *testing.T) {
132132

133133
func TestSpecificSeek(t *testing.T) {
134134
ledgerSize := 5
135-
rl := ramledger.New(ledgerSize, genesisBlock)
135+
_, rl := ramledger.New(ledgerSize, genesisBlock)
136136
for i := 1; i < ledgerSize; i++ {
137137
rl.Append([]*cb.Envelope{&cb.Envelope{Payload: []byte(fmt.Sprintf("%d", i))}}, nil)
138138
}
@@ -160,7 +160,7 @@ func TestSpecificSeek(t *testing.T) {
160160

161161
func TestBadSeek(t *testing.T) {
162162
ledgerSize := 5
163-
rl := ramledger.New(ledgerSize, genesisBlock)
163+
_, rl := ramledger.New(ledgerSize, genesisBlock)
164164
for i := 1; i < 2*ledgerSize; i++ {
165165
rl.Append([]*cb.Envelope{&cb.Envelope{Payload: []byte(fmt.Sprintf("%d", i))}}, nil)
166166
}
@@ -196,7 +196,7 @@ func TestBadSeek(t *testing.T) {
196196

197197
func TestBadWindow(t *testing.T) {
198198
ledgerSize := 5
199-
rl := ramledger.New(ledgerSize, genesisBlock)
199+
_, rl := ramledger.New(ledgerSize, genesisBlock)
200200

201201
m := newMockD()
202202
defer close(m.recvChan)
@@ -219,7 +219,7 @@ func TestBadWindow(t *testing.T) {
219219
func TestAck(t *testing.T) {
220220
ledgerSize := 10
221221
windowSize := uint64(2)
222-
rl := ramledger.New(ledgerSize, genesisBlock)
222+
_, rl := ramledger.New(ledgerSize, genesisBlock)
223223
for i := 1; i < ledgerSize; i++ {
224224
rl.Append([]*cb.Envelope{&cb.Envelope{Payload: []byte(fmt.Sprintf("%d", i))}}, nil)
225225
}

orderer/main.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -186,11 +186,11 @@ func launchSolo(conf *config.TopLevel) {
186186
}
187187
}
188188

189-
rawledger = fileledger.New(location, genesisBlock)
189+
_, rawledger = fileledger.New(location, genesisBlock)
190190
case "ram":
191191
fallthrough
192192
default:
193-
rawledger = ramledger.New(int(conf.RAMLedger.HistorySize), genesisBlock)
193+
_, rawledger = ramledger.New(int(conf.RAMLedger.HistorySize), genesisBlock)
194194
}
195195

196196
lastConfigTx := retrieveConfiguration(rawledger)

orderer/rawledger/blackbox_test.go

+71-15
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package rawledger_test
1818

1919
import (
2020
"bytes"
21+
"reflect"
2122
"testing"
2223

2324
. "github.com/hyperledger/fabric/orderer/rawledger"
@@ -26,12 +27,12 @@ import (
2627
)
2728

2829
type ledgerTestable interface {
29-
Initialize() (ledgerFactory, error)
30+
Initialize() (ledgerTestFactory, error)
3031
Name() string
3132
}
3233

33-
type ledgerFactory interface {
34-
New() ReadWriter
34+
type ledgerTestFactory interface {
35+
New() (Factory, ReadWriter)
3536
Destroy() error
3637
Persistent() bool
3738
}
@@ -52,7 +53,7 @@ func getBlock(number uint64, li ReadWriter) *cb.Block {
5253
}
5354
}
5455

55-
func allTest(t *testing.T, test func(ledgerFactory, *testing.T)) {
56+
func allTest(t *testing.T, test func(ledgerTestFactory, *testing.T)) {
5657
for _, lt := range testables {
5758

5859
t.Log("Running test for", lt.Name())
@@ -77,8 +78,8 @@ func TestInitialization(t *testing.T) {
7778
allTest(t, testInitialization)
7879
}
7980

80-
func testInitialization(lf ledgerFactory, t *testing.T) {
81-
li := lf.New()
81+
func testInitialization(lf ledgerTestFactory, t *testing.T) {
82+
_, li := lf.New()
8283
if li.Height() != 1 {
8384
t.Fatalf("Block height should be 1")
8485
}
@@ -92,14 +93,14 @@ func TestReinitialization(t *testing.T) {
9293
allTest(t, testReinitialization)
9394
}
9495

95-
func testReinitialization(lf ledgerFactory, t *testing.T) {
96+
func testReinitialization(lf ledgerTestFactory, t *testing.T) {
9697
if !lf.Persistent() {
9798
t.Log("Skipping test as persistence is not available for this ledger type")
9899
return
99100
}
100-
oli := lf.New()
101+
_, oli := lf.New()
101102
aBlock := oli.Append([]*cb.Envelope{&cb.Envelope{Payload: []byte("My Data")}}, nil)
102-
li := lf.New()
103+
_, li := lf.New()
103104
if li.Height() != 2 {
104105
t.Fatalf("Block height should be 2")
105106
}
@@ -116,8 +117,8 @@ func TestAddition(t *testing.T) {
116117
allTest(t, testAddition)
117118
}
118119

119-
func testAddition(lf ledgerFactory, t *testing.T) {
120-
li := lf.New()
120+
func testAddition(lf ledgerTestFactory, t *testing.T) {
121+
_, li := lf.New()
121122
genesis := getBlock(0, li)
122123
if genesis == nil {
123124
t.Fatalf("Could not retrieve genesis block")
@@ -141,8 +142,8 @@ func TestRetrieval(t *testing.T) {
141142
allTest(t, testRetrieval)
142143
}
143144

144-
func testRetrieval(lf ledgerFactory, t *testing.T) {
145-
li := lf.New()
145+
func testRetrieval(lf ledgerTestFactory, t *testing.T) {
146+
_, li := lf.New()
146147
li.Append([]*cb.Envelope{&cb.Envelope{Payload: []byte("My Data")}}, nil)
147148
it, num := li.Iterator(ab.SeekInfo_OLDEST, 99)
148149
if num != 0 {
@@ -180,8 +181,8 @@ func TestBlockedRetrieval(t *testing.T) {
180181
allTest(t, testBlockedRetrieval)
181182
}
182183

183-
func testBlockedRetrieval(lf ledgerFactory, t *testing.T) {
184-
li := lf.New()
184+
func testBlockedRetrieval(lf ledgerTestFactory, t *testing.T) {
185+
_, li := lf.New()
185186
it, num := li.Iterator(ab.SeekInfo_SPECIFIED, 1)
186187
if num != 1 {
187188
t.Fatalf("Expected block iterator at 1, but got %d", num)
@@ -206,3 +207,58 @@ func testBlockedRetrieval(lf ledgerFactory, t *testing.T) {
206207
t.Fatalf("Expected to successfully retrieve the second block")
207208
}
208209
}
210+
211+
func TestMultichain(t *testing.T) {
212+
allTest(t, testMultichain)
213+
}
214+
215+
func testMultichain(lf ledgerTestFactory, t *testing.T) {
216+
f, _ := lf.New()
217+
chain1 := []byte("chain1")
218+
chain2 := []byte("chain2")
219+
220+
c1p1 := []byte("c1 payload1")
221+
c1p2 := []byte("c1 payload2")
222+
223+
c2p1 := []byte("c2 payload1")
224+
225+
c1, err := f.GetOrCreate(chain1)
226+
if err != nil {
227+
t.Fatalf("Error creating chain1: %s", err)
228+
}
229+
230+
c1.Append([]*cb.Envelope{&cb.Envelope{Payload: c1p1}}, nil)
231+
c1b1 := c1.Append([]*cb.Envelope{&cb.Envelope{Payload: c1p2}}, nil)
232+
233+
if c1.Height() != 2 {
234+
t.Fatalf("Block height for c1 should be 2")
235+
}
236+
237+
c2, err := f.GetOrCreate(chain2)
238+
if err != nil {
239+
t.Fatalf("Error creating chain2: %s", err)
240+
}
241+
c2b0 := c2.Append([]*cb.Envelope{&cb.Envelope{Payload: c2p1}}, nil)
242+
243+
if c2.Height() != 1 {
244+
t.Fatalf("Block height for c2 should be 1")
245+
}
246+
247+
c1, err = f.GetOrCreate(chain1)
248+
if err != nil {
249+
t.Fatalf("Error retrieving chain1: %s", err)
250+
}
251+
252+
if b := getBlock(1, c1); !reflect.DeepEqual(c1b1, b) {
253+
t.Fatalf("Did not properly store block 1 on chain 1:")
254+
}
255+
256+
c2, err = f.GetOrCreate(chain1)
257+
if err != nil {
258+
t.Fatalf("Error retrieving chain2: %s", err)
259+
}
260+
261+
if b := getBlock(0, c2); reflect.DeepEqual(c2b0, b) {
262+
t.Fatalf("Did not properly store block 1 on chain 1")
263+
}
264+
}

orderer/rawledger/fileledger/fileledger.go

+82-6
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import (
2020
"fmt"
2121
"io/ioutil"
2222
"os"
23+
"reflect"
24+
"sync"
2325

2426
"github.com/hyperledger/fabric/orderer/rawledger"
2527
cb "github.com/hyperledger/fabric/protos/common"
@@ -55,21 +57,92 @@ type fileLedger struct {
5557
marshaler *jsonpb.Marshaler
5658
}
5759

58-
// New creates a new instance of the file ledger
59-
func New(directory string, genesisBlock *cb.Block) rawledger.ReadWriter {
60+
type fileLedgerFactory struct {
61+
directory string
62+
ledgers map[string]rawledger.ReadWriter
63+
mutex sync.Mutex
64+
}
65+
66+
func New(directory string, systemGenesis *cb.Block) (rawledger.Factory, rawledger.ReadWriter) {
67+
env := &cb.Envelope{}
68+
err := proto.Unmarshal(systemGenesis.Data.Data[0], env)
69+
if err != nil {
70+
logger.Fatalf("Bad envelope in genesis block: %s", err)
71+
}
72+
73+
payload := &cb.Payload{}
74+
err = proto.Unmarshal(env.Payload, payload)
75+
if err != nil {
76+
logger.Fatalf("Bad payload in genesis block: %s", err)
77+
}
78+
6079
logger.Debugf("Initializing fileLedger at '%s'", directory)
6180
if err := os.MkdirAll(directory, 0700); err != nil {
62-
panic(err)
81+
logger.Fatalf("Could not create directory %s: %s", directory, err)
6382
}
83+
84+
flf := &fileLedgerFactory{
85+
directory: directory,
86+
ledgers: make(map[string]rawledger.ReadWriter),
87+
}
88+
89+
flt, err := flf.GetOrCreate(payload.Header.ChainHeader.ChainID)
90+
if err != nil {
91+
logger.Fatalf("Error getting orderer system chain dir: %s", err)
92+
}
93+
94+
fl := flt.(*fileLedger)
95+
96+
if fl.height > 0 {
97+
block, ok := fl.readBlock(0)
98+
if !ok {
99+
logger.Fatalf("Error reading genesis block for chain of height %d", fl.height)
100+
}
101+
if !reflect.DeepEqual(block, systemGenesis) {
102+
logger.Fatalf("Attempted to reconfigure an existing ordering system chain with new genesis block")
103+
}
104+
} else {
105+
fl.writeBlock(systemGenesis)
106+
fl.height = 1
107+
fl.lastHash = systemGenesis.Header.Hash()
108+
}
109+
110+
return flf, fl
111+
}
112+
113+
func (flf *fileLedgerFactory) GetOrCreate(chainID []byte) (rawledger.ReadWriter, error) {
114+
flf.mutex.Lock()
115+
defer flf.mutex.Unlock()
116+
117+
key := string(chainID)
118+
119+
// Check a second time with the lock held
120+
l, ok := flf.ledgers[key]
121+
if ok {
122+
return l, nil
123+
}
124+
125+
directory := fmt.Sprintf("%s/%x", flf.directory, chainID)
126+
127+
logger.Debugf("Initializing chain at '%s'", directory)
128+
129+
if err := os.MkdirAll(directory, 0700); err != nil {
130+
return nil, err
131+
}
132+
133+
ch := newChain(directory)
134+
flf.ledgers[key] = ch
135+
return ch, nil
136+
}
137+
138+
// newChain creates a new chain backed by a file ledger
139+
func newChain(directory string) rawledger.ReadWriter {
64140
fl := &fileLedger{
65141
directory: directory,
66142
fqFormatString: directory + "/" + blockFileFormatString,
67143
signal: make(chan struct{}),
68144
marshaler: &jsonpb.Marshaler{Indent: " "},
69145
}
70-
if _, err := os.Stat(fl.blockFilename(genesisBlock.Header.Number)); os.IsNotExist(err) {
71-
fl.writeBlock(genesisBlock)
72-
}
73146
fl.initializeBlockHeight()
74147
logger.Debugf("Initialized to block height %d with hash %x", fl.height-1, fl.lastHash)
75148
return fl
@@ -97,6 +170,9 @@ func (fl *fileLedger) initializeBlockHeight() {
97170
nextNumber++
98171
}
99172
fl.height = nextNumber
173+
if fl.height == 0 {
174+
return
175+
}
100176
block, found := fl.readBlock(fl.height - 1)
101177
if !found {
102178
panic(fmt.Errorf("Block %d was in directory listing but error reading", fl.height-1))

orderer/rawledger/fileledger/fileledger_test.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,12 @@ type testEnv struct {
4444
}
4545

4646
func initialize(t *testing.T) (*testEnv, *fileLedger) {
47-
name, err := ioutil.TempDir("", "hyperledger")
47+
name, err := ioutil.TempDir("", "hyperledger_fabric")
4848
if err != nil {
4949
t.Fatalf("Error creating temp dir: %s", err)
5050
}
51-
return &testEnv{location: name, t: t}, New(name, genesisBlock).(*fileLedger)
51+
_, fl := New(name, genesisBlock)
52+
return &testEnv{location: name, t: t}, fl.(*fileLedger)
5253
}
5354

5455
func (tev *testEnv) tearDown() {
@@ -77,7 +78,8 @@ func TestReinitialization(t *testing.T) {
7778
tev, ofl := initialize(t)
7879
defer tev.tearDown()
7980
ofl.Append([]*cb.Envelope{&cb.Envelope{Payload: []byte("My Data")}}, nil)
80-
fl := New(tev.location, genesisBlock).(*fileLedger)
81+
_, flt := New(tev.location, genesisBlock)
82+
fl := flt.(*fileLedger)
8183
if fl.height != 2 {
8284
t.Fatalf("Block height should be 2")
8385
}

0 commit comments

Comments
 (0)