Skip to content

Commit 4e0f96b

Browse files
committed
create ledger with genesis block
https://jira.hyperledger.org/browse/FAB-2676 This CR introduces a new function in the ledgermgmt package `CreateWithGenesisBlock(genesisBlock *common.Block)` which allows the ledger creation and committing of genesis block atomically. This can be used for peer joining a channel with the genesis block with the config tx. Change-Id: I8f90a2b623aabca27c35448bc6eb6bc13e2dbd90 Signed-off-by: manish <[email protected]>
1 parent 51b7e85 commit 4e0f96b

File tree

4 files changed

+223
-10
lines changed

4 files changed

+223
-10
lines changed

core/ledger/kvledger/kv_ledger_provider.go

+150-10
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ limitations under the License.
1717
package kvledger
1818

1919
import (
20+
"bytes"
2021
"errors"
22+
"fmt"
2123

2224
"github.com/hyperledger/fabric/common/ledger/blkstorage"
2325
"github.com/hyperledger/fabric/common/ledger/blkstorage/fsblkstorage"
@@ -29,6 +31,9 @@ import (
2931
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb/statecouchdb"
3032
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb/stateleveldb"
3133
"github.com/hyperledger/fabric/core/ledger/ledgerconfig"
34+
"github.com/hyperledger/fabric/protos/common"
35+
"github.com/hyperledger/fabric/protos/utils"
36+
"github.com/syndtr/goleveldb/leveldb"
3237
)
3338

3439
var (
@@ -38,6 +43,9 @@ var (
3843
ErrNonExistingLedgerID = errors.New("LedgerID does not exist")
3944
// ErrLedgerNotOpened is thrown by a CloseLedger call if a ledger with the given id has not been opened
4045
ErrLedgerNotOpened = errors.New("Ledger is not opened yet")
46+
47+
underConstructionLedgerKey = []byte("underConstructionLedgerKey")
48+
ledgerKeyPrefix = []byte("l")
4149
)
4250

4351
// Provider implements interface ledger.PeerLedgerProvider
@@ -90,7 +98,44 @@ func NewProvider() (ledger.PeerLedgerProvider, error) {
9098
historydbProvider = historyleveldb.NewHistoryDBProvider()
9199

92100
logger.Info("ledger provider Initialized")
93-
return &Provider{idStore, blockStoreProvider, vdbProvider, historydbProvider}, nil
101+
provider := &Provider{idStore, blockStoreProvider, vdbProvider, historydbProvider}
102+
provider.recoverUnderConstructionLedger()
103+
return provider, nil
104+
}
105+
106+
// CreateWithGenesisBlock implements the corresponding method from interface ledger.PeerLedgerProvider
107+
// This functions sets a under construction flag before doing any thing related to ledger creation and
108+
// upon a successful ledger creation with the committed genesis block, removes the flag and add entry into
109+
// created ledgers list (atomically). If a crash happens in between, the 'recoverUnderConstructionLedger'
110+
// function is invoked before declaring the provider to be usable
111+
func (provider *Provider) CreateWithGenesisBlock(genesisBlock *common.Block) (ledger.PeerLedger, error) {
112+
ledgerID, err := utils.GetChainIDFromBlock(genesisBlock)
113+
if err != nil {
114+
return nil, err
115+
}
116+
exists, err := provider.idStore.ledgerIDExists(ledgerID)
117+
if err != nil {
118+
return nil, err
119+
}
120+
if exists {
121+
return nil, ErrLedgerIDExists
122+
}
123+
if err = provider.idStore.setUnderConstructionFlag(ledgerID); err != nil {
124+
return nil, err
125+
}
126+
ledger, err := provider.openInternal(ledgerID)
127+
if err != nil {
128+
logger.Errorf("Error in opening a new empty ledger. Unsetting under construction flag. Err: %s", err)
129+
panicOnErr(provider.runCleanup(ledgerID), "Error while running cleanup for ledger id [%s]", ledgerID)
130+
panicOnErr(provider.idStore.unsetUnderConstructionFlag(), "Error while unsetting under construction flag")
131+
return nil, err
132+
}
133+
if err := ledger.Commit(genesisBlock); err != nil {
134+
ledger.Close()
135+
return nil, err
136+
}
137+
panicOnErr(provider.idStore.createLedgerID(ledgerID), "Error while marking ledger as created")
138+
return ledger, nil
94139
}
95140

96141
// Create implements the corresponding method from interface ledger.PeerLedgerProvider
@@ -102,15 +147,20 @@ func (provider *Provider) Create(ledgerID string) (ledger.PeerLedger, error) {
102147
if exists {
103148
return nil, ErrLedgerIDExists
104149
}
105-
provider.idStore.createLedgerID(ledgerID)
106-
return provider.Open(ledgerID)
150+
ledger, err := provider.openInternal(ledgerID)
151+
if err != nil {
152+
return nil, err
153+
}
154+
if err = provider.idStore.createLedgerID(ledgerID); err != nil {
155+
ledger.Close()
156+
return nil, err
157+
}
158+
return ledger, nil
107159
}
108160

109161
// Open implements the corresponding method from interface ledger.PeerLedgerProvider
110162
func (provider *Provider) Open(ledgerID string) (ledger.PeerLedger, error) {
111-
112163
logger.Debugf("Open() opening kvledger: %s", ledgerID)
113-
114164
// Check the ID store to ensure that the chainId/ledgerId exists
115165
exists, err := provider.idStore.ledgerIDExists(ledgerID)
116166
if err != nil {
@@ -119,7 +169,10 @@ func (provider *Provider) Open(ledgerID string) (ledger.PeerLedger, error) {
119169
if !exists {
120170
return nil, ErrNonExistingLedgerID
121171
}
172+
return provider.openInternal(ledgerID)
173+
}
122174

175+
func (provider *Provider) openInternal(ledgerID string) (ledger.PeerLedger, error) {
123176
// Get the block store for a chain/ledger
124177
blockStore, err := provider.blockStoreProvider.OpenBlockStore(ledgerID)
125178
if err != nil {
@@ -165,6 +218,63 @@ func (provider *Provider) Close() {
165218
provider.historydbProvider.Close()
166219
}
167220

221+
// recoverUnderConstructionLedger checks whether the under construction flag is set - this would be the case
222+
// if a crash had happened during creation of ledger and the ledger creation could have been left in intermediate
223+
// state. Recovery checks if the ledger was created and the genesis block was committed successfully then it completes
224+
// the last step of adding the ledger id to the list of created ledgers. Else, it clears the under construction flag
225+
func (provider *Provider) recoverUnderConstructionLedger() {
226+
logger.Debugf("Recovering under construction ledger")
227+
ledgerID, err := provider.idStore.getUnderConstructionFlag()
228+
panicOnErr(err, "Error while checking whether the under construction flag is set")
229+
if ledgerID == "" {
230+
logger.Debugf("No under construction ledger found. Quitting recovery")
231+
return
232+
}
233+
logger.Infof("ledger [%s] found as under construction", ledgerID)
234+
ledger, err := provider.openInternal(ledgerID)
235+
panicOnErr(err, "Error while opening under construction ledger [%s]", ledgerID)
236+
bcInfo, err := ledger.GetBlockchainInfo()
237+
panicOnErr(err, "Error while getting blockchain info for the under construction ledger [%s]", ledgerID)
238+
ledger.Close()
239+
240+
switch bcInfo.Height {
241+
case 0:
242+
logger.Infof("Genesis block was not committed. Hence, the peer ledger not created. unsetting the under construction flag")
243+
panicOnErr(provider.runCleanup(ledgerID), "Error while running cleanup for ledger id [%s]", ledgerID)
244+
panicOnErr(provider.idStore.unsetUnderConstructionFlag(), "Error while unsetting under construction flag")
245+
case 1:
246+
logger.Infof("Genesis block was committed. Hence, marking the peer ledger as created")
247+
panicOnErr(provider.idStore.createLedgerID(ledgerID), "Error while adding ledgerID [%s] to created list", ledgerID)
248+
default:
249+
panic(fmt.Errorf(
250+
"Data inconsistency: under construction flag is set for ledger [%s] while the height of the blockchain is [%d]",
251+
ledgerID, bcInfo.Height))
252+
}
253+
return
254+
}
255+
256+
// runCleanup cleans up blockstorage, statedb, and historydb for what
257+
// may have got created during in-complete ledger creation
258+
func (provider *Provider) runCleanup(ledgerID string) error {
259+
// TODO - though, not having this is harmless for kv ledger.
260+
// If we want, following could be done:
261+
// - blockstorage could remove empty folders
262+
// - couchdb backed statedb could delete the database if got created
263+
// - leveldb backed statedb and history db need not perform anything as it uses a single db shared across ledgers
264+
return nil
265+
}
266+
267+
func panicOnErr(err error, mgsFormat string, args ...interface{}) {
268+
if err == nil {
269+
return
270+
}
271+
args = append(args, err)
272+
panic(fmt.Sprintf(mgsFormat+" Err:%s ", args...))
273+
}
274+
275+
//////////////////////////////////////////////////////////////////////
276+
// Ledger id persistence related code
277+
///////////////////////////////////////////////////////////////////////
168278
type idStore struct {
169279
db *leveldbhelper.DB
170280
}
@@ -175,8 +285,24 @@ func openIDStore(path string) *idStore {
175285
return &idStore{db}
176286
}
177287

288+
func (s *idStore) setUnderConstructionFlag(ledgerID string) error {
289+
return s.db.Put(underConstructionLedgerKey, []byte(ledgerID), true)
290+
}
291+
292+
func (s *idStore) unsetUnderConstructionFlag() error {
293+
return s.db.Delete(underConstructionLedgerKey, true)
294+
}
295+
296+
func (s *idStore) getUnderConstructionFlag() (string, error) {
297+
val, err := s.db.Get(underConstructionLedgerKey)
298+
if err != nil {
299+
return "", err
300+
}
301+
return string(val), nil
302+
}
303+
178304
func (s *idStore) createLedgerID(ledgerID string) error {
179-
key := []byte(ledgerID)
305+
key := s.encodeLedgerKey(ledgerID)
180306
val := []byte{}
181307
err := error(nil)
182308
if val, err = s.db.Get(key); err != nil {
@@ -185,11 +311,14 @@ func (s *idStore) createLedgerID(ledgerID string) error {
185311
if val != nil {
186312
return ErrLedgerIDExists
187313
}
188-
return s.db.Put(key, val, true)
314+
batch := &leveldb.Batch{}
315+
batch.Put(key, val)
316+
batch.Delete(underConstructionLedgerKey)
317+
return s.db.WriteBatch(batch, true)
189318
}
190319

191320
func (s *idStore) ledgerIDExists(ledgerID string) (bool, error) {
192-
key := []byte(ledgerID)
321+
key := s.encodeLedgerKey(ledgerID)
193322
val := []byte{}
194323
err := error(nil)
195324
if val, err = s.db.Get(key); err != nil {
@@ -203,8 +332,11 @@ func (s *idStore) getAllLedgerIds() ([]string, error) {
203332
itr := s.db.GetIterator(nil, nil)
204333
itr.First()
205334
for itr.Valid() {
206-
key := string(itr.Key())
207-
ids = append(ids, key)
335+
if bytes.Equal(itr.Key(), underConstructionLedgerKey) {
336+
continue
337+
}
338+
id := string(s.decodeLedgerID(itr.Key()))
339+
ids = append(ids, id)
208340
itr.Next()
209341
}
210342
return ids, nil
@@ -213,3 +345,11 @@ func (s *idStore) getAllLedgerIds() ([]string, error) {
213345
func (s *idStore) close() {
214346
s.db.Close()
215347
}
348+
349+
func (s *idStore) encodeLedgerKey(ledgerID string) []byte {
350+
return append(ledgerKeyPrefix, []byte(ledgerID)...)
351+
}
352+
353+
func (s *idStore) decodeLedgerID(key []byte) string {
354+
return string(key[len(ledgerKeyPrefix):])
355+
}

core/ledger/kvledger/kv_ledger_provider_test.go

+42
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"fmt"
2121
"testing"
2222

23+
configtxtest "github.com/hyperledger/fabric/common/configtx/test"
2324
"github.com/hyperledger/fabric/common/ledger/testutil"
2425
"github.com/hyperledger/fabric/core/ledger"
2526
)
@@ -56,6 +57,47 @@ func TestLedgerProvider(t *testing.T) {
5657
testutil.AssertEquals(t, err, ErrNonExistingLedgerID)
5758
}
5859

60+
func TestLedgerCreationWithGenesisBlock(t *testing.T) {
61+
env := newTestEnv(t)
62+
defer env.cleanup()
63+
numLedgers := 10
64+
provider, _ := NewProvider()
65+
existingLedgerIDs, err := provider.List()
66+
testutil.AssertNoError(t, err, "")
67+
testutil.AssertEquals(t, len(existingLedgerIDs), 0)
68+
for i := 0; i < numLedgers; i++ {
69+
genesisBlock, _ := configtxtest.MakeGenesisBlock(constructTestLedgerID(i))
70+
provider.CreateWithGenesisBlock(genesisBlock)
71+
}
72+
existingLedgerIDs, err = provider.List()
73+
testutil.AssertNoError(t, err, "")
74+
testutil.AssertEquals(t, len(existingLedgerIDs), numLedgers)
75+
76+
provider.Close()
77+
78+
provider, _ = NewProvider()
79+
defer provider.Close()
80+
ledgerIds, _ := provider.List()
81+
testutil.AssertEquals(t, len(ledgerIds), numLedgers)
82+
t.Logf("ledgerIDs=%#v", ledgerIds)
83+
for i := 0; i < numLedgers; i++ {
84+
testutil.AssertEquals(t, ledgerIds[i], constructTestLedgerID(i))
85+
}
86+
for i := 0; i < numLedgers; i++ {
87+
ledger, err := provider.Open(constructTestLedgerID(i))
88+
testutil.AssertNoError(t, err, "")
89+
bcInfo, err := ledger.GetBlockchainInfo()
90+
ledger.Close()
91+
testutil.AssertNoError(t, err, "")
92+
testutil.AssertEquals(t, bcInfo.Height, uint64(1))
93+
}
94+
_, err = provider.Create(constructTestLedgerID(2))
95+
testutil.AssertEquals(t, err, ErrLedgerIDExists)
96+
97+
_, err = provider.Open(constructTestLedgerID(numLedgers))
98+
testutil.AssertEquals(t, err, ErrNonExistingLedgerID)
99+
}
100+
59101
func TestMultipleLedgerBasicRW(t *testing.T) {
60102
env := newTestEnv(t)
61103
defer env.cleanup()

core/ledger/ledger_interface.go

+4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ import (
2626
type PeerLedgerProvider interface {
2727
// Create creates a new ledger with a given unique id
2828
Create(ledgerID string) (PeerLedger, error)
29+
// CreateWithGenesisBlock creates a new ledger with the given genesis block.
30+
// This function guarentees that the creation of ledger and committing the genesis block would an atomic action
31+
// The chain id retrieved from the genesis block is treated as a ledger id
32+
CreateWithGenesisBlock(genesisBlock *common.Block) (PeerLedger, error)
2933
// Open opens an already created ledger
3034
Open(ledgerID string) (PeerLedger, error)
3135
// Exists tells whether the ledger with given id exists

core/ledger/ledgermgmt/ledger_mgmt.go

+27
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import (
2424

2525
"github.com/hyperledger/fabric/core/ledger"
2626
"github.com/hyperledger/fabric/core/ledger/kvledger"
27+
"github.com/hyperledger/fabric/protos/common"
28+
"github.com/hyperledger/fabric/protos/utils"
2729
logging "github.com/op/go-logging"
2830
)
2931

@@ -80,6 +82,31 @@ func CreateLedger(id string) (ledger.PeerLedger, error) {
8082
return l, nil
8183
}
8284

85+
// CreateWithGenesisBlock creates a new ledger with the given genesis block.
86+
// This function guarentees that the creation of ledger and committing the genesis block would an atomic action
87+
// The chain id retrieved from the genesis block is treated as a ledger id
88+
func CreateWithGenesisBlock(genesisBlock *common.Block) (ledger.PeerLedger, error) {
89+
lock.Lock()
90+
defer lock.Unlock()
91+
if !initialized {
92+
return nil, ErrLedgerMgmtNotInitialized
93+
}
94+
id, err := utils.GetChainIDFromBlock(genesisBlock)
95+
if err != nil {
96+
return nil, err
97+
}
98+
99+
logger.Infof("Creating ledger [%s] with genesis block", id)
100+
l, err := ledgerProvider.CreateWithGenesisBlock(genesisBlock)
101+
if err != nil {
102+
return nil, err
103+
}
104+
l = wrapLedger(id, l)
105+
openedLedgers[id] = l
106+
logger.Infof("Created ledger [%s] with genesis block", id)
107+
return l, nil
108+
}
109+
83110
// OpenLedger returns a ledger for the given id
84111
func OpenLedger(id string) (ledger.PeerLedger, error) {
85112
logger.Infof("Opening ledger with id = %s", id)

0 commit comments

Comments
 (0)