diff --git a/api/coreservice.go b/api/coreservice.go index 2c928a99bd..565e73747c 100644 --- a/api/coreservice.go +++ b/api/coreservice.go @@ -119,7 +119,7 @@ type ( // ActionsByAddress returns all actions associated with an address ActionsByAddress(addr address.Address, start uint64, count uint64) ([]*iotexapi.ActionInfo, error) // ActionByActionHash returns action by action hash - ActionByActionHash(h hash.Hash256) (*action.SealedEnvelope, hash.Hash256, uint64, uint32, error) + ActionByActionHash(h hash.Hash256) (*action.SealedEnvelope, *block.Block, uint32, error) // PendingActionByActionHash returns action by action hash PendingActionByActionHash(h hash.Hash256) (*action.SealedEnvelope, error) // ActPoolActions returns the all Transaction Identifiers in the actpool @@ -1089,24 +1089,24 @@ func (core *coreService) BlockHashByBlockHeight(blkHeight uint64) (hash.Hash256, } // ActionByActionHash returns action by action hash -func (core *coreService) ActionByActionHash(h hash.Hash256) (*action.SealedEnvelope, hash.Hash256, uint64, uint32, error) { +func (core *coreService) ActionByActionHash(h hash.Hash256) (*action.SealedEnvelope, *block.Block, uint32, error) { if err := core.checkActionIndex(); err != nil { - return nil, hash.ZeroHash256, 0, 0, status.Error(codes.NotFound, blockindex.ErrActionIndexNA.Error()) + return nil, nil, 0, status.Error(codes.NotFound, blockindex.ErrActionIndexNA.Error()) } actIndex, err := core.indexer.GetActionIndex(h[:]) if err != nil { - return nil, hash.ZeroHash256, 0, 0, errors.Wrap(ErrNotFound, err.Error()) + return nil, nil, 0, errors.Wrap(ErrNotFound, err.Error()) } blk, err := core.dao.GetBlockByHeight(actIndex.BlockHeight()) if err != nil { - return nil, hash.ZeroHash256, 0, 0, errors.Wrap(ErrNotFound, err.Error()) + return nil, nil, 0, errors.Wrap(ErrNotFound, err.Error()) } selp, index, err := blk.ActionByHash(h) if err != nil { - return nil, hash.ZeroHash256, 0, 0, errors.Wrap(ErrNotFound, err.Error()) + return nil, nil, 0, errors.Wrap(ErrNotFound, err.Error()) } - return selp, blk.HashBlock(), actIndex.BlockHeight(), index, nil + return selp, blk, index, nil } // ActionByActionHash returns action by action hash @@ -1290,9 +1290,9 @@ func (core *coreService) pendingAction(selp *action.SealedEnvelope) (*iotexapi.A } func (core *coreService) getAction(actHash hash.Hash256, checkPending bool) (*iotexapi.ActionInfo, error) { - selp, blkHash, blkHeight, actIndex, err := core.ActionByActionHash(actHash) + selp, blk, actIndex, err := core.ActionByActionHash(actHash) if err == nil { - act, err := core.committedAction(selp, blkHash, blkHeight) + act, err := core.committedAction(selp, blk.HashBlock(), blk.Height()) if err != nil { return nil, err } diff --git a/api/grpcserver_integrity_test.go b/api/grpcserver_integrity_test.go index 06092d1e9e..955e6336d1 100644 --- a/api/grpcserver_integrity_test.go +++ b/api/grpcserver_integrity_test.go @@ -2344,7 +2344,7 @@ func TestGrpcServer_GetActionByActionHashIntegrity(t *testing.T) { }() for _, test := range _getActionByActionHashTest { - ret, _, _, _, err := svr.core.ActionByActionHash(test.h) + ret, _, _, err := svr.core.ActionByActionHash(test.h) require.NoError(err) require.Equal(test.expectedNounce, ret.Envelope.Nonce()) } diff --git a/api/web3server.go b/api/web3server.go index adce43041b..67336accac 100644 --- a/api/web3server.go +++ b/api/web3server.go @@ -585,11 +585,11 @@ func (svr *web3Handler) getTransactionByHash(in *gjson.Result) (interface{}, err return nil, err } - selp, blkHash, _, _, err := svr.coreService.ActionByActionHash(actHash) + selp, blk, _, err := svr.coreService.ActionByActionHash(actHash) if err == nil { receipt, err := svr.coreService.ReceiptByActionHash(actHash) if err == nil { - return svr.assembleConfirmedTransaction(blkHash, selp, receipt) + return svr.assembleConfirmedTransaction(blk.HashBlock(), selp, receipt) } if errors.Cause(err) == ErrNotFound { return nil, nil @@ -629,7 +629,7 @@ func (svr *web3Handler) getTransactionReceipt(in *gjson.Result) (interface{}, er } // acquire action receipt by action hash - selp, blockHash, _, _, err := svr.coreService.ActionByActionHash(actHash) + selp, blk, _, err := svr.coreService.ActionByActionHash(actHash) if err != nil { if errors.Cause(err) == ErrNotFound { return nil, nil @@ -649,19 +649,13 @@ func (svr *web3Handler) getTransactionReceipt(in *gjson.Result) (interface{}, er } // acquire logsBloom from blockMeta - blkHash := hex.EncodeToString(blockHash[:]) - blk, err := svr.coreService.BlockByHash(blkHash) - if err != nil { - return nil, err - } - var logsBloomStr string - if logsBloom := blk.Block.LogsBloomfilter(); logsBloom != nil { + if logsBloom := blk.LogsBloomfilter(); logsBloom != nil { logsBloomStr = hex.EncodeToString(logsBloom.Bytes()) } return &getReceiptResult{ - blockHash: blockHash, + blockHash: blk.HashBlock(), from: selp.SenderAddress(), to: to, contractAddress: contractAddr, diff --git a/api/web3server_test.go b/api/web3server_test.go index 82fa9355c6..354d8ee278 100644 --- a/api/web3server_test.go +++ b/api/web3server_test.go @@ -626,7 +626,15 @@ func TestGetTransactionByHash(t *testing.T) { ContractAddress: "test", TxIndex: 1, } - core.EXPECT().ActionByActionHash(gomock.Any()).Return(selp, hash.Hash256b([]byte("test")), uint64(0), uint32(0), nil) + blk, err := block.NewTestingBuilder(). + SetHeight(1). + SetVersion(111). + SetPrevBlockHash(hash.ZeroHash256). + SetTimeStamp(time.Now()). + AddActions(selp). + SignAndBuild(identityset.PrivateKey(0)) + require.NoError(err) + core.EXPECT().ActionByActionHash(gomock.Any()).Return(selp, &blk, uint32(0), nil) core.EXPECT().ReceiptByActionHash(gomock.Any()).Return(receipt, nil) core.EXPECT().EVMNetworkID().Return(uint32(0)) @@ -642,7 +650,7 @@ func TestGetTransactionByHash(t *testing.T) { require.Equal(receipt, rlt.receipt) // get pending transaction - core.EXPECT().ActionByActionHash(gomock.Any()).Return(nil, hash.ZeroHash256, uint64(0), uint32(0), ErrNotFound) + core.EXPECT().ActionByActionHash(gomock.Any()).Return(nil, nil, uint32(0), ErrNotFound) core.EXPECT().PendingActionByActionHash(gomock.Any()).Return(selp, nil) core.EXPECT().EVMNetworkID().Return(uint32(0)) ret, err = web3svr.getTransactionByHash(&in) @@ -657,7 +665,7 @@ func TestGetTransactionByHash(t *testing.T) { require.NoError(err) txHash, err = selp.Hash() require.NoError(err) - core.EXPECT().ActionByActionHash(gomock.Any()).Return(nil, hash.ZeroHash256, uint64(0), uint32(0), ErrNotFound) + core.EXPECT().ActionByActionHash(gomock.Any()).Return(nil, nil, uint32(0), ErrNotFound) core.EXPECT().PendingActionByActionHash(gomock.Any()).Return(selp, nil) core.EXPECT().EVMNetworkID().Return(uint32(0)) ret, err = web3svr.getTransactionByHash(&in) @@ -737,8 +745,6 @@ func TestGetTransactionReceipt(t *testing.T) { ContractAddress: "test", TxIndex: 1, } - core.EXPECT().ActionByActionHash(gomock.Any()).Return(selp, hash.Hash256b([]byte("test")), uint64(0), uint32(0), nil) - core.EXPECT().ReceiptByActionHash(gomock.Any()).Return(receipt, nil) blk, err := block.NewTestingBuilder(). SetHeight(1). SetVersion(111). @@ -747,9 +753,8 @@ func TestGetTransactionReceipt(t *testing.T) { AddActions(selp). SignAndBuild(identityset.PrivateKey(0)) require.NoError(err) - core.EXPECT().BlockByHash(gomock.Any()).Return(&apitypes.BlockWithReceipts{ - Block: &blk, - }, nil) + core.EXPECT().ActionByActionHash(gomock.Any()).Return(selp, &blk, uint32(0), nil) + core.EXPECT().ReceiptByActionHash(gomock.Any()).Return(receipt, nil) t.Run("nil params", func(t *testing.T) { inNil := gjson.Parse(`{"params":[]}`) diff --git a/blockchain/block/block_deserializer.go b/blockchain/block/block_deserializer.go index 6c69e6a545..a59f096b79 100644 --- a/blockchain/block/block_deserializer.go +++ b/blockchain/block/block_deserializer.go @@ -101,25 +101,36 @@ func (bd *Deserializer) DeserializeBody(buf []byte) (*Body, error) { return &b, nil } -// FromBlockStoreProto converts protobuf to block store -func (bd *Deserializer) FromBlockStoreProto(pb *iotextypes.BlockStore) (*Store, error) { - in := &Store{} +func (bd *Deserializer) BlockFromBlockStoreProto(pb *iotextypes.BlockStore) (*Block, error) { + return bd.blockFromBlockStoreProto(pb) +} + +func (bd *Deserializer) blockFromBlockStoreProto(pb *iotextypes.BlockStore) (*Block, error) { blk, err := bd.FromBlockProto(pb.Block) if err != nil { return nil, err } - // verify merkle root can match after deserialize - if err := blk.VerifyTxRoot(); err != nil { - return nil, err - } + // TODO: Reenable this if necessary + // // verify merkle root can match after deserialize + // if err := blk.VerifyTxRoot(); err != nil { + // return nil, err + // } + return blk, nil +} + +func (bd *Deserializer) ReceiptsFromBlockStoreProto(pb *iotextypes.BlockStore) ([]*action.Receipt, error) { + return bd.receiptsFromBlockStoreProto(pb) +} - in.Block = blk +func (bd *Deserializer) receiptsFromBlockStoreProto(pb *iotextypes.BlockStore) ([]*action.Receipt, error) { + receipts := make([]*action.Receipt, 0) for _, receiptPb := range pb.Receipts { receipt := &action.Receipt{} receipt.ConvertFromReceiptPb(receiptPb) - in.Receipts = append(in.Receipts, receipt) + receipts = append(receipts, receipt) } - return in, nil + + return receipts, nil } // DeserializeBlockStore de-serializes a block store @@ -128,5 +139,16 @@ func (bd *Deserializer) DeserializeBlockStore(buf []byte) (*Store, error) { if err := proto.Unmarshal(buf, &pb); err != nil { return nil, errors.Wrap(err, "failed to unmarshal block store") } - return bd.FromBlockStoreProto(&pb) + blk, err := bd.blockFromBlockStoreProto(&pb) + if err != nil { + return nil, err + } + receipts, err := bd.receiptsFromBlockStoreProto(&pb) + if err != nil { + return nil, err + } + return &Store{ + Block: blk, + Receipts: receipts, + }, nil } diff --git a/blockchain/block/block_deserializer_test.go b/blockchain/block/block_deserializer_test.go index b0f4f2fa59..0b9b1b1b35 100644 --- a/blockchain/block/block_deserializer_test.go +++ b/blockchain/block/block_deserializer_test.go @@ -45,10 +45,10 @@ func TestBlockStoreDeserializer(t *testing.T) { require.NotNil(storeProto) bd := Deserializer{} - store1, err := bd.FromBlockStoreProto(storeProto) + store1, err := bd.BlockFromBlockStoreProto(storeProto) require.NoError(err) - require.Equal(store1.Block.height, store.Block.height) - require.Equal(store1.Block.Header.prevBlockHash, store.Block.Header.prevBlockHash) - require.Equal(store1.Block.Header.blockSig, store.Block.Header.blockSig) + require.Equal(store1.height, store.Block.height) + require.Equal(store1.Header.prevBlockHash, store.Block.Header.prevBlockHash) + require.Equal(store1.Header.blockSig, store.Block.Header.blockSig) } diff --git a/blockchain/filedao/filedao_v2.go b/blockchain/filedao/filedao_v2.go index e275a1275a..91e630807f 100644 --- a/blockchain/filedao/filedao_v2.go +++ b/blockchain/filedao/filedao_v2.go @@ -37,17 +37,19 @@ var ( type ( // fileDAOv2 handles chain db file after file split activation at v1.1.2 fileDAOv2 struct { - filename string - header *FileHeader - tip *FileTip - blkBuffer *stagingBuffer - blkCache cache.LRUCache - kvStore db.KVStore - batch batch.KVStoreBatch - hashStore db.CountingIndex // store block hash - blkStore db.CountingIndex // store raw blocks - sysStore db.CountingIndex // store transaction log - deser *block.Deserializer + filename string + header *FileHeader + tip *FileTip + blkBuffer *stagingBuffer + blkStorePbCache cache.LRUCache + blkCache cache.LRUCache + receiptCache cache.LRUCache + kvStore db.KVStore + batch batch.KVStoreBatch + hashStore db.CountingIndex // store block hash + blkStore db.CountingIndex // store raw blocks + sysStore db.CountingIndex // store transaction log + deser *block.Deserializer } ) @@ -68,10 +70,12 @@ func newFileDAOv2(bottom uint64, cfg db.Config, deser *block.Deserializer) (*fil tip: &FileTip{ Height: bottom - 1, }, - blkCache: cache.NewThreadSafeLruCache(16), - kvStore: db.NewBoltDB(cfg), - batch: batch.NewBatch(), - deser: deser, + blkStorePbCache: cache.NewThreadSafeLruCache(16), + blkCache: cache.NewThreadSafeLruCache(256), + receiptCache: cache.NewThreadSafeLruCache(256), + kvStore: db.NewBoltDB(cfg), + batch: batch.NewBatch(), + deser: deser, } return &fd, nil } @@ -79,11 +83,13 @@ func newFileDAOv2(bottom uint64, cfg db.Config, deser *block.Deserializer) (*fil // openFileDAOv2 opens an existing v2 file func openFileDAOv2(cfg db.Config, deser *block.Deserializer) *fileDAOv2 { return &fileDAOv2{ - filename: cfg.DbPath, - blkCache: cache.NewThreadSafeLruCache(16), - kvStore: db.NewBoltDB(cfg), - batch: batch.NewBatch(), - deser: deser, + filename: cfg.DbPath, + blkStorePbCache: cache.NewThreadSafeLruCache(16), + blkCache: cache.NewThreadSafeLruCache(256), + receiptCache: cache.NewThreadSafeLruCache(256), + kvStore: db.NewBoltDB(cfg), + batch: batch.NewBatch(), + deser: deser, } } @@ -185,19 +191,19 @@ func (fd *fileDAOv2) GetBlockByHeight(height uint64) (*block.Block, error) { if height == 0 { return block.GenesisBlock(), nil } - blkInfo, err := fd.getBlockStore(height) + blk, err := fd.getBlock(height) if err != nil { return nil, errors.Wrapf(err, "failed to get block at height %d", height) } - return blkInfo.Block, nil + return blk, nil } func (fd *fileDAOv2) GetReceipts(height uint64) ([]*action.Receipt, error) { - blkInfo, err := fd.getBlockStore(height) + receipts, err := fd.getReceipt(height) if err != nil { return nil, errors.Wrapf(err, "failed to get receipts at height %d", height) } - return blkInfo.Receipts, nil + return receipts, nil } func (fd *fileDAOv2) ContainsTransactionLog() bool { diff --git a/blockchain/filedao/filedao_v2_util.go b/blockchain/filedao/filedao_v2_util.go index 6f6e0aa532..ea24e01fae 100644 --- a/blockchain/filedao/filedao_v2_util.go +++ b/blockchain/filedao/filedao_v2_util.go @@ -10,6 +10,7 @@ import ( "github.com/iotexproject/iotex-proto/golang/iotextypes" + "github.com/iotexproject/iotex-core/action" "github.com/iotexproject/iotex-core/blockchain/block" "github.com/iotexproject/iotex-core/db" "github.com/iotexproject/iotex-core/db/batch" @@ -172,23 +173,76 @@ func (fd *fileDAOv2) highestBlockOfStoreTip() uint64 { return fd.header.Start + fd.blkStore.Size()*fd.header.BlockStoreSize - 1 } -func (fd *fileDAOv2) getBlockStore(height uint64) (*block.Store, error) { +func (fd *fileDAOv2) getBlock(height uint64) (*block.Block, error) { if !fd.ContainsHeight(height) { return nil, db.ErrNotExist } + // check whether block in staging buffer or not + storeKey := blockStoreKey(height, fd.header) + if storeKey >= fd.blkStore.Size() { + blkStore, err := fd.blkBuffer.Get(stagingKey(height, fd.header)) + if err != nil { + return nil, err + } + return blkStore.Block, nil + } + // check whether block in read cache or not + if value, ok := fd.blkCache.Get(height); ok { + return value.(*block.Block), nil + } + // read from storage DB + blockStore, err := fd.getBlockStore(height) + if err != nil { + return nil, err + } + blk, err := fd.deser.BlockFromBlockStoreProto(blockStore) + if err != nil { + return nil, err + } + // add to read cache + fd.blkCache.Add(height, blk) + return blk, nil +} +func (fd *fileDAOv2) getReceipt(height uint64) ([]*action.Receipt, error) { + if !fd.ContainsHeight(height) { + return nil, db.ErrNotExist + } // check whether block in staging buffer or not storeKey := blockStoreKey(height, fd.header) if storeKey >= fd.blkStore.Size() { - return fd.blkBuffer.Get(stagingKey(height, fd.header)) + blkStore, err := fd.blkBuffer.Get(stagingKey(height, fd.header)) + if err != nil { + return nil, err + } + return blkStore.Receipts, nil + } + // check whether receipts in read cache or not + if value, ok := fd.receiptCache.Get(height); ok { + return value.([]*action.Receipt), nil + } + // read from storage DB + blockStore, err := fd.getBlockStore(height) + if err != nil { + return nil, err } + receipts, err := fd.deser.ReceiptsFromBlockStoreProto(blockStore) + if err != nil { + return nil, err + } + // add to read cache + fd.receiptCache.Add(height, receipts) + return receipts, nil +} - // check whether block in read cache or not - if value, ok := fd.blkCache.Get(storeKey); ok { +func (fd *fileDAOv2) getBlockStore(height uint64) (*iotextypes.BlockStore, error) { + // check whether blockStore in read cache or not + storeKey := blockStoreKey(height, fd.header) + if value, ok := fd.blkStorePbCache.Get(storeKey); ok { pbInfos := value.(*iotextypes.BlockStores) - return fd.deser.FromBlockStoreProto(pbInfos.BlockStores[stagingKey(height, fd.header)]) + return pbInfos.BlockStores[stagingKey(height, fd.header)], nil } - + // read from storage DB value, err := fd.blkStore.Get(storeKey) if err != nil { return nil, err @@ -204,8 +258,7 @@ func (fd *fileDAOv2) getBlockStore(height uint64) (*block.Store, error) { if len(pbStores.BlockStores) != int(fd.header.BlockStoreSize) { return nil, ErrDataCorruption } - // add to read cache - fd.blkCache.Add(storeKey, pbStores) - return fd.deser.FromBlockStoreProto(pbStores.BlockStores[stagingKey(height, fd.header)]) + fd.blkStorePbCache.Add(storeKey, pbStores) + return pbStores.BlockStores[stagingKey(height, fd.header)], nil } diff --git a/blockchain/filedao/testing.go b/blockchain/filedao/testing.go index 6afc3805f6..925cb7b575 100644 --- a/blockchain/filedao/testing.go +++ b/blockchain/filedao/testing.go @@ -66,10 +66,12 @@ func newFileDAOv2InMem(bottom uint64) (*fileDAOv2, error) { tip: &FileTip{ Height: bottom - 1, }, - blkCache: cache.NewThreadSafeLruCache(16), - kvStore: db.NewMemKVStore(), - batch: batch.NewBatch(), - deser: block.NewDeserializer(_defaultEVMNetworkID), + blkStorePbCache: cache.NewThreadSafeLruCache(16), + blkCache: cache.NewThreadSafeLruCache(256), + receiptCache: cache.NewThreadSafeLruCache(256), + kvStore: db.NewMemKVStore(), + batch: batch.NewBatch(), + deser: block.NewDeserializer(_defaultEVMNetworkID), } return &fd, nil } diff --git a/test/mock/mock_apicoreservice/mock_apicoreservice.go b/test/mock/mock_apicoreservice/mock_apicoreservice.go index 0528dd1eca..4ebb8bd255 100644 --- a/test/mock/mock_apicoreservice/mock_apicoreservice.go +++ b/test/mock/mock_apicoreservice/mock_apicoreservice.go @@ -78,15 +78,14 @@ func (mr *MockCoreServiceMockRecorder) Action(actionHash, checkPending interface } // ActionByActionHash mocks base method. -func (m *MockCoreService) ActionByActionHash(h hash.Hash256) (*action.SealedEnvelope, hash.Hash256, uint64, uint32, error) { +func (m *MockCoreService) ActionByActionHash(h hash.Hash256) (*action.SealedEnvelope, *block.Block, uint32, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ActionByActionHash", h) ret0, _ := ret[0].(*action.SealedEnvelope) - ret1, _ := ret[1].(hash.Hash256) - ret2, _ := ret[2].(uint64) - ret3, _ := ret[3].(uint32) - ret4, _ := ret[4].(error) - return ret0, ret1, ret2, ret3, ret4 + ret1, _ := ret[1].(*block.Block) + ret2, _ := ret[2].(uint32) + ret3, _ := ret[3].(error) + return ret0, ret1, ret2, ret3 } // ActionByActionHash indicates an expected call of ActionByActionHash.