Skip to content

Commit c717c4b

Browse files
author
Ruslan Kryukov
committed
[FAB-2086] API method to get block by TxID
https://jira.hyperledger.org/browse/FAB-2086 We need this method for getting information of block in a ledger by txID. Also this method helps to check valid tx or not by getting the validation bit array from the block. Change-Id: I4216635097ecd4cf5de9e3442dfbd5660d44292f Signed-off-by: Ruslan Kryukov <[email protected]>
1 parent c341fe5 commit c717c4b

12 files changed

+128
-0
lines changed

common/ledger/blkstorage/blockstorage.go

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const (
3232
IndexableAttrBlockHash = IndexableAttr("BlockHash")
3333
IndexableAttrTxID = IndexableAttr("TxID")
3434
IndexableAttrBlockNumTranNum = IndexableAttr("BlockNumTranNum")
35+
IndexableAttrBlockTxID = IndexableAttr("BlockTxID")
3536
)
3637

3738
// IndexConfig - a configuration that includes a list of attributes that should be indexed
@@ -66,5 +67,6 @@ type BlockStore interface {
6667
RetrieveBlockByNumber(blockNum uint64) (*common.Block, error) // blockNum of math.MaxUint64 will return last block
6768
RetrieveTxByID(txID string) (*common.Envelope, error)
6869
RetrieveTxByBlockNumTranNum(blockNum uint64, tranNum uint64) (*common.Envelope, error)
70+
RetrieveBlockByTxID(txID string) (*common.Block, error)
6971
Shutdown()
7072
}

common/ledger/blkstorage/fsblkstorage/blockfile_mgr.go

+11
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,17 @@ func (mgr *blockfileMgr) retrieveBlockByNumber(blockNum uint64) (*common.Block,
438438
return mgr.fetchBlock(loc)
439439
}
440440

441+
func (mgr *blockfileMgr) retrieveBlockByTxID(txID string) (*common.Block, error) {
442+
logger.Debugf("retrieveBlockByTxID() - txID = [%s]", txID)
443+
444+
loc, err := mgr.index.getBlockLocByTxID(txID)
445+
446+
if err != nil {
447+
return nil, err
448+
}
449+
return mgr.fetchBlock(loc)
450+
}
451+
441452
func (mgr *blockfileMgr) retrieveBlockHeaderByNumber(blockNum uint64) (*common.BlockHeader, error) {
442453
logger.Debugf("retrieveBlockHeaderByNumber() - blockNum = [%d]", blockNum)
443454
loc, err := mgr.index.getBlockLocByBlockNum(blockNum)

common/ledger/blkstorage/fsblkstorage/blockfile_mgr_test.go

+20
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,23 @@ func TestBlockfileMgrFileRolling(t *testing.T) {
216216
testutil.AssertEquals(t, blkfileMgrWrapper.blockfileMgr.cpInfo.latestFileChunkSuffixNum, 2)
217217
blkfileMgrWrapper.testGetBlockByHash(blocks)
218218
}
219+
220+
func TestBlockfileMgrGetBlockByTxID(t *testing.T) {
221+
env := newTestEnv(t, NewConf(testPath, 0))
222+
defer env.Cleanup()
223+
blkfileMgrWrapper := newTestBlockfileWrapper(env, "testLedger")
224+
defer blkfileMgrWrapper.close()
225+
blocks := testutil.ConstructTestBlocks(t, 10)
226+
blkfileMgrWrapper.addBlocks(blocks)
227+
for _, blk := range blocks {
228+
for j, _ := range blk.Data.Data {
229+
// blockNum starts with 1
230+
txID, err := extractTxID(blk.Data.Data[j])
231+
testutil.AssertNoError(t, err, "")
232+
233+
blockFromFileMgr, err := blkfileMgrWrapper.blockfileMgr.retrieveBlockByTxID(txID)
234+
testutil.AssertNoError(t, err, "Error while retrieving block from blkfileMgr")
235+
testutil.AssertEquals(t, blockFromFileMgr, blk)
236+
}
237+
}
238+
}

common/ledger/blkstorage/fsblkstorage/blockindex.go

+29
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const (
3131
blockHashIdxKeyPrefix = 'h'
3232
txIDIdxKeyPrefix = 't'
3333
blockNumTranNumIdxKeyPrefix = 'a'
34+
blockTxIDIdxKeyPrefix = 'b'
3435
indexCheckpointKeyStr = "indexCheckpointKey"
3536
)
3637

@@ -43,6 +44,7 @@ type index interface {
4344
getBlockLocByBlockNum(blockNum uint64) (*fileLocPointer, error)
4445
getTxLoc(txID string) (*fileLocPointer, error)
4546
getTXLocByBlockNumTranNum(blockNum uint64, tranNum uint64) (*fileLocPointer, error)
47+
getBlockLocByTxID(txID string) (*fileLocPointer, error)
4648
}
4749

4850
type blockIdxInfo struct {
@@ -127,6 +129,13 @@ func (index *blockIndex) indexBlock(blockIdxInfo *blockIdxInfo) error {
127129
}
128130
}
129131

132+
// Index5 - Store BlockNumber will be used to find block by transaction id
133+
if _, ok := index.indexItemsMap[blkstorage.IndexableAttrBlockTxID]; ok {
134+
for _, txoffset := range txOffsets {
135+
batch.Put(constructBlockTxIDKey(txoffset.txID), flpBytes)
136+
}
137+
}
138+
130139
batch.Put(indexCheckpointKey, encodeBlockNum(blockIdxInfo.blockNum))
131140
if err := index.db.WriteBatch(batch, false); err != nil {
132141
return err
@@ -182,6 +191,22 @@ func (index *blockIndex) getTxLoc(txID string) (*fileLocPointer, error) {
182191
return txFLP, nil
183192
}
184193

194+
func (index *blockIndex) getBlockLocByTxID(txID string) (*fileLocPointer, error) {
195+
if _, ok := index.indexItemsMap[blkstorage.IndexableAttrBlockTxID]; !ok {
196+
return nil, blkstorage.ErrAttrNotIndexed
197+
}
198+
b, err := index.db.Get(constructBlockTxIDKey(txID))
199+
if err != nil {
200+
return nil, err
201+
}
202+
if b == nil {
203+
return nil, blkstorage.ErrNotFoundInIndex
204+
}
205+
txFLP := &fileLocPointer{}
206+
txFLP.unmarshal(b)
207+
return txFLP, nil
208+
}
209+
185210
func (index *blockIndex) getTXLocByBlockNumTranNum(blockNum uint64, tranNum uint64) (*fileLocPointer, error) {
186211
if _, ok := index.indexItemsMap[blkstorage.IndexableAttrBlockNumTranNum]; !ok {
187212
return nil, blkstorage.ErrAttrNotIndexed
@@ -211,6 +236,10 @@ func constructTxIDKey(txID string) []byte {
211236
return append([]byte{txIDIdxKeyPrefix}, []byte(txID)...)
212237
}
213238

239+
func constructBlockTxIDKey(txID string) []byte {
240+
return append([]byte{blockTxIDIdxKeyPrefix}, []byte(txID)...)
241+
}
242+
214243
func constructBlockNumTranNumKey(blockNum uint64, txNum uint64) []byte {
215244
blkNumBytes := util.EncodeOrderPreservingVarUint64(blockNum)
216245
tranNumBytes := util.EncodeOrderPreservingVarUint64(txNum)

common/ledger/blkstorage/fsblkstorage/blockindex_test.go

+16
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ func (i *noopIndex) getTXLocByBlockNumTranNum(blockNum uint64, tranNum uint64) (
4747
return nil, nil
4848
}
4949

50+
func (i *noopIndex) getBlockLocByTxID(txID string) (*fileLocPointer, error) {
51+
return nil, nil
52+
}
53+
5054
func TestBlockIndexSync(t *testing.T) {
5155
testBlockIndexSync(t, 10, 5, false)
5256
testBlockIndexSync(t, 10, 5, true)
@@ -111,6 +115,7 @@ func TestBlockIndexSelectiveIndexing(t *testing.T) {
111115
testBlockIndexSelectiveIndexing(t, []blkstorage.IndexableAttr{blkstorage.IndexableAttrBlockNumTranNum})
112116
testBlockIndexSelectiveIndexing(t, []blkstorage.IndexableAttr{blkstorage.IndexableAttrBlockHash, blkstorage.IndexableAttrBlockNum})
113117
testBlockIndexSelectiveIndexing(t, []blkstorage.IndexableAttr{blkstorage.IndexableAttrTxID, blkstorage.IndexableAttrBlockNumTranNum})
118+
testBlockIndexSelectiveIndexing(t, []blkstorage.IndexableAttr{blkstorage.IndexableAttrBlockTxID})
114119
}
115120

116121
func testBlockIndexSelectiveIndexing(t *testing.T, indexItems []blkstorage.IndexableAttr) {
@@ -168,4 +173,15 @@ func testBlockIndexSelectiveIndexing(t *testing.T, indexItems []blkstorage.Index
168173
} else {
169174
testutil.AssertSame(t, err, blkstorage.ErrAttrNotIndexed)
170175
}
176+
177+
// test 'retrieveBlockByTxID'
178+
txid, err = extractTxID(blocks[0].Data.Data[0])
179+
testutil.AssertNoError(t, err, "")
180+
block, err = blockfileMgr.retrieveBlockByTxID(txid)
181+
if testutil.Contains(indexItems, blkstorage.IndexableAttrBlockTxID) {
182+
testutil.AssertNoError(t, err, "Error while retrieving block by txID")
183+
testutil.AssertEquals(t, blocks[0], block)
184+
} else {
185+
testutil.AssertSame(t, err, blkstorage.ErrAttrNotIndexed)
186+
}
171187
}

common/ledger/blkstorage/fsblkstorage/fs_blockstore.go

+4
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ func (store *fsBlockStore) RetrieveTxByBlockNumTranNum(blockNum uint64, tranNum
7777
return store.fileMgr.retrieveTransactionByBlockNumTranNum(blockNum, tranNum)
7878
}
7979

80+
func (store *fsBlockStore) RetrieveBlockByTxID(txID string) (*common.Block, error) {
81+
return store.fileMgr.retrieveBlockByTxID(txID)
82+
}
83+
8084
// Shutdown shuts down the block store
8185
func (store *fsBlockStore) Shutdown() {
8286
logger.Debugf("closing fs blockStore:%s", store.id)

common/ledger/blkstorage/fsblkstorage/pkg_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ func newTestEnv(t testing.TB, conf *Conf) *testEnv {
4141
blkstorage.IndexableAttrBlockNum,
4242
blkstorage.IndexableAttrTxID,
4343
blkstorage.IndexableAttrBlockNumTranNum,
44+
blkstorage.IndexableAttrBlockTxID,
4445
}
4546
return newTestEnvSelectiveIndexing(t, conf, attrsToIndex)
4647
}

core/ledger/kvledger/kv_ledger.go

+5
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,11 @@ func (l *kvLedger) GetBlockByHash(blockHash []byte) (*common.Block, error) {
228228
return l.blockStore.RetrieveBlockByHash(blockHash)
229229
}
230230

231+
// GetBlockByTxID returns a block which contains a transaction
232+
func (l *kvLedger) GetBlockByTxID(txID string) (*common.Block, error) {
233+
return l.blockStore.RetrieveBlockByTxID(txID)
234+
}
235+
231236
//Prune prunes the blocks/transactions that satisfy the given policy
232237
func (l *kvLedger) Prune(policy commonledger.PrunePolicy) error {
233238
return errors.New("Not yet implemented")

core/ledger/kvledger/kv_ledger_provider.go

+1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ func NewProvider() (ledger.PeerLedgerProvider, error) {
6363
blkstorage.IndexableAttrBlockNum,
6464
blkstorage.IndexableAttrTxID,
6565
blkstorage.IndexableAttrBlockNumTranNum,
66+
blkstorage.IndexableAttrBlockTxID,
6667
}
6768
indexConfig := &blkstorage.IndexConfig{AttrsToIndex: attrsToIndex}
6869
blockStoreProvider := fsblkstorage.NewProvider(

core/ledger/ledger_interface.go

+2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ type PeerLedger interface {
4343
GetTransactionByID(txID string) (*common.Envelope, error)
4444
// GetBlockByHash returns a block given it's hash
4545
GetBlockByHash(blockHash []byte) (*common.Block, error)
46+
// GetBlockByTxID returns a block which contains a transaction
47+
GetBlockByTxID(txID string) (*common.Block, error)
4648
// NewTxSimulator gives handle to a transaction simulator.
4749
// A client can obtain more than one 'TxSimulator's for parallel execution.
4850
// Any snapshoting/synchronization should be performed at the implementation level if required

core/scc/qscc/querier.go

+20
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ const (
4545
GetBlockByNumber string = "GetBlockByNumber"
4646
GetBlockByHash string = "GetBlockByHash"
4747
GetTransactionByID string = "GetTransactionByID"
48+
GetBlockByTxID string = "GetBlockByTxID"
4849
)
4950

5051
// Init is called once per chain when the chain is created.
@@ -95,6 +96,8 @@ func (e *LedgerQuerier) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
9596
return getBlockByHash(targetLedger, args[2])
9697
case GetChainInfo:
9798
return getChainInfo(targetLedger)
99+
case GetBlockByTxID:
100+
return getBlockByTxID(targetLedger, args[2])
98101
}
99102

100103
return shim.Error(fmt.Sprintf("Requested function %s not found.", fname))
@@ -177,3 +180,20 @@ func getChainInfo(vledger ledger.PeerLedger) pb.Response {
177180

178181
return shim.Success(bytes)
179182
}
183+
184+
func getBlockByTxID(vledger ledger.PeerLedger, rawTxID []byte) pb.Response {
185+
txID := string(rawTxID)
186+
block, err := vledger.GetBlockByTxID(txID)
187+
188+
if err != nil {
189+
return shim.Error(fmt.Sprintf("Failed to get block for txID %s, error %s", txID, err))
190+
}
191+
192+
bytes, err := utils.Marshal(block)
193+
194+
if err != nil {
195+
return shim.Error(err.Error())
196+
}
197+
198+
return shim.Success(bytes)
199+
}

core/scc/qscc/querier_test.go

+17
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,20 @@ func TestQueryGetBlockByHash(t *testing.T) {
117117
t.Fatalf("qscc GetBlockByHash should have failed with invalid hash: 0")
118118
}
119119
}
120+
121+
func TestQueryGetBlockByTxID(t *testing.T) {
122+
viper.Set("peer.fileSystemPath", "/var/hyperledger/test8/")
123+
defer os.RemoveAll("/var/hyperledger/test8/")
124+
peer.MockInitialize()
125+
peer.MockCreateChain("mytestchainid8")
126+
127+
e := new(LedgerQuerier)
128+
stub := shim.NewMockStub("LedgerQuerier", e)
129+
130+
txID := ""
131+
132+
args := [][]byte{[]byte(GetBlockByTxID), []byte("mytestchainid8"), []byte(txID)}
133+
if res := stub.MockInvoke("1", args); res.Status == shim.OK {
134+
t.Fatalf("qscc GetBlockByTxID should have failed with invalid txID: %s", txID)
135+
}
136+
}

0 commit comments

Comments
 (0)