From 95632092a1360a11816a9edae9565dbce9c29d63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nina=20/=20=E1=83=9C=E1=83=98=E1=83=9C=E1=83=90?= Date: Thu, 15 Aug 2024 15:45:31 +0200 Subject: [PATCH] feat(store): add rawlog in txstatus (#1458) ## Description Fixes #1454 --------- Co-authored-by: Rootul P --- consensus/replay_test.go | 2 +- proto/tendermint/store/types.pb.go | 70 ++++++++++++++++++++++++++---- proto/tendermint/store/types.proto | 5 ++- rpc/core/blocks_test.go | 2 +- state/execution.go | 12 ++++- state/execution_test.go | 2 +- state/mocks/block_store.go | 8 ++-- state/services.go | 2 +- store/store.go | 13 +++++- store/store_test.go | 16 +++++-- 10 files changed, 109 insertions(+), 23 deletions(-) diff --git a/consensus/replay_test.go b/consensus/replay_test.go index efba30e27a..90a7710ab4 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -1224,7 +1224,7 @@ func (bs *mockBlockStore) LoadBlockMeta(height int64) *types.BlockMeta { func (bs *mockBlockStore) LoadBlockPart(height int64, index int) *types.Part { return nil } func (bs *mockBlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) { } -func (bs *mockBlockStore) SaveTxInfo(block *types.Block, txResponseCode []uint32) error { +func (bs *mockBlockStore) SaveTxInfo(block *types.Block, txResponseCodes []uint32, logs []string) error { return nil } func (bs *mockBlockStore) LoadTxInfo(hash []byte) *cmtstore.TxInfo { return &cmtstore.TxInfo{} } diff --git a/proto/tendermint/store/types.pb.go b/proto/tendermint/store/types.pb.go index bdc1c5cc26..e780fa50f8 100644 --- a/proto/tendermint/store/types.pb.go +++ b/proto/tendermint/store/types.pb.go @@ -75,13 +75,16 @@ func (m *BlockStoreState) GetHeight() int64 { } // TxInfo describes the location of a tx inside a committed block -// as well as the result of executing the transaction. +// as well as the result of executing the transaction and the raw log output. type TxInfo struct { Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` Index uint32 `protobuf:"varint,2,opt,name=index,proto3" json:"index,omitempty"` // The response code of executing the tx. 0 means // successfully executed, all others are error codes. Code uint32 `protobuf:"varint,3,opt,name=code,proto3" json:"code,omitempty"` + // The log output generated during the execution of a transaction. + // Note: this is empty if the transaction succeeded. + Log string `protobuf:"bytes,4,opt,name=log,proto3" json:"log,omitempty"` } func (m *TxInfo) Reset() { *m = TxInfo{} } @@ -138,6 +141,13 @@ func (m *TxInfo) GetCode() uint32 { return 0 } +func (m *TxInfo) GetLog() string { + if m != nil { + return m.Log + } + return "" +} + func init() { proto.RegisterType((*BlockStoreState)(nil), "tendermint.store.BlockStoreState") proto.RegisterType((*TxInfo)(nil), "tendermint.store.TxInfo") @@ -146,20 +156,21 @@ func init() { func init() { proto.RegisterFile("tendermint/store/types.proto", fileDescriptor_ff9e53a0a74267f7) } var fileDescriptor_ff9e53a0a74267f7 = []byte{ - // 207 bytes of a gzipped FileDescriptorProto + // 223 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x29, 0x49, 0xcd, 0x4b, 0x49, 0x2d, 0xca, 0xcd, 0xcc, 0x2b, 0xd1, 0x2f, 0x2e, 0xc9, 0x2f, 0x4a, 0xd5, 0x2f, 0xa9, 0x2c, 0x48, 0x2d, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x40, 0xc8, 0xea, 0x81, 0x65, 0x95, 0x6c, 0xb9, 0xf8, 0x9d, 0x72, 0xf2, 0x93, 0xb3, 0x83, 0x41, 0xbc, 0xe0, 0x92, 0xc4, 0x92, 0x54, 0x21, 0x21, 0x2e, 0x96, 0xa4, 0xc4, 0xe2, 0x54, 0x09, 0x46, 0x05, 0x46, 0x0d, 0xe6, 0x20, 0x30, 0x5b, 0x48, 0x8c, 0x8b, 0x2d, 0x23, 0x35, 0x33, 0x3d, 0xa3, 0x44, 0x82, 0x09, 0x2c, 0x0a, 0xe5, - 0x29, 0x79, 0x71, 0xb1, 0x85, 0x54, 0x78, 0xe6, 0xa5, 0xe5, 0x23, 0xa9, 0x60, 0x44, 0x56, 0x21, + 0x29, 0xc5, 0x70, 0xb1, 0x85, 0x54, 0x78, 0xe6, 0xa5, 0xe5, 0x23, 0xa9, 0x60, 0x44, 0x56, 0x21, 0x24, 0xc2, 0xc5, 0x9a, 0x99, 0x97, 0x92, 0x5a, 0x01, 0xd6, 0xc8, 0x1b, 0x04, 0xe1, 0x80, 0xec, - 0x48, 0xce, 0x4f, 0x49, 0x95, 0x60, 0x06, 0x0b, 0x82, 0xd9, 0x4e, 0x81, 0x27, 0x1e, 0xc9, 0x31, - 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0xe3, 0x84, 0xc7, 0x72, 0x0c, 0x17, 0x1e, 0xcb, - 0x31, 0xdc, 0x78, 0x2c, 0xc7, 0x10, 0x65, 0x9e, 0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, - 0x9f, 0xab, 0x8f, 0xe4, 0x3f, 0x24, 0x26, 0xd8, 0x7b, 0xfa, 0xe8, 0x7e, 0x4f, 0x62, 0x03, 0x8b, - 0x1b, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x87, 0xbd, 0xb8, 0x53, 0x16, 0x01, 0x00, 0x00, + 0x48, 0xce, 0x4f, 0x49, 0x95, 0x60, 0x06, 0x0b, 0x82, 0xd9, 0x42, 0x02, 0x5c, 0xcc, 0x39, 0xf9, + 0xe9, 0x12, 0x2c, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, 0x20, 0xa6, 0x53, 0xe0, 0x89, 0x47, 0x72, 0x8c, + 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, 0x1c, 0xc3, 0x85, 0xc7, 0x72, + 0x0c, 0x37, 0x1e, 0xcb, 0x31, 0x44, 0x99, 0xa7, 0x67, 0x96, 0x64, 0x94, 0x26, 0xe9, 0x25, 0xe7, + 0xe7, 0xea, 0x23, 0xf9, 0x18, 0x89, 0x09, 0xf6, 0xb0, 0x3e, 0x7a, 0x68, 0x24, 0xb1, 0x81, 0xc5, + 0x8d, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xfb, 0x4b, 0xe7, 0xbe, 0x28, 0x01, 0x00, 0x00, } func (m *BlockStoreState) Marshal() (dAtA []byte, err error) { @@ -215,6 +226,13 @@ func (m *TxInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.Log) > 0 { + i -= len(m.Log) + copy(dAtA[i:], m.Log) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Log))) + i-- + dAtA[i] = 0x22 + } if m.Code != 0 { i = encodeVarintTypes(dAtA, i, uint64(m.Code)) i-- @@ -274,6 +292,10 @@ func (m *TxInfo) Size() (n int) { if m.Code != 0 { n += 1 + sovTypes(uint64(m.Code)) } + l = len(m.Log) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } return n } @@ -457,6 +479,38 @@ func (m *TxInfo) Unmarshal(dAtA []byte) error { break } } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Log", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Log = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) diff --git a/proto/tendermint/store/types.proto b/proto/tendermint/store/types.proto index c11572f22e..998c92483f 100644 --- a/proto/tendermint/store/types.proto +++ b/proto/tendermint/store/types.proto @@ -9,11 +9,14 @@ message BlockStoreState { } // TxInfo describes the location of a tx inside a committed block -// as well as the result of executing the transaction. +// as well as the result of executing the transaction and the raw log output. message TxInfo { int64 height = 1; uint32 index = 2; // The response code of executing the tx. 0 means // successfully executed, all others are error codes. uint32 code = 3; + // The log output generated during the execution of a transaction. + // Note: this is empty if the transaction succeeded. + string log = 4; } diff --git a/rpc/core/blocks_test.go b/rpc/core/blocks_test.go index 41ea5efeed..ed945b6a0f 100644 --- a/rpc/core/blocks_test.go +++ b/rpc/core/blocks_test.go @@ -301,7 +301,7 @@ func (mockBlockStore) LoadSeenCommit(height int64) *types.Commit { retur func (mockBlockStore) PruneBlocks(height int64) (uint64, error) { return 0, nil } func (mockBlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) { } -func (mockBlockStore) SaveTxInfo(block *types.Block, txResponseCode []uint32) error { +func (mockBlockStore) SaveTxInfo(block *types.Block, txResponseCodes []uint32, logs []string) error { return nil } diff --git a/state/execution.go b/state/execution.go index 54a5ad8dcb..5ba6706cb5 100644 --- a/state/execution.go +++ b/state/execution.go @@ -248,7 +248,8 @@ func (blockExec *BlockExecutor) ApplyBlock( // for correct crash recovery if blockExec.blockStore != nil { respCodes := getResponseCodes(abciResponses.DeliverTxs) - if err := blockExec.blockStore.SaveTxInfo(block, respCodes); err != nil { + logs := getLogs(abciResponses.DeliverTxs) + if err := blockExec.blockStore.SaveTxInfo(block, respCodes, logs); err != nil { return state, 0, err } } @@ -684,3 +685,12 @@ func getResponseCodes(responses []*abci.ResponseDeliverTx) []uint32 { } return responseCodes } + +// getLogs gets logs from a list of ResponseDeliverTx. +func getLogs(responses []*abci.ResponseDeliverTx) []string { + logs := make([]string, len(responses)) + for i, response := range responses { + logs[i] = response.Log + } + return logs +} diff --git a/state/execution_test.go b/state/execution_test.go index b389f6c9a9..f4e18bb67b 100644 --- a/state/execution_test.go +++ b/state/execution_test.go @@ -93,7 +93,7 @@ func TestApplyBlockWithBlockStore(t *testing.T) { blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()} // Check that SaveTxInfo is called with correct arguments - blockStore.On("SaveTxInfo", block, mock.AnythingOfType("[]uint32")).Return(nil) + blockStore.On("SaveTxInfo", block, mock.AnythingOfType("[]uint32"), mock.AnythingOfType("[]string")).Return(nil) _, _, err = blockExec.ApplyBlock(state, blockID, block, nil) require.Nil(t, err) diff --git a/state/mocks/block_store.go b/state/mocks/block_store.go index b726c5e0c1..63a3e2c7fd 100644 --- a/state/mocks/block_store.go +++ b/state/mocks/block_store.go @@ -208,12 +208,12 @@ func (_m *BlockStore) PruneBlocks(height int64) (uint64, error) { } // SaveTxInfo provides a mock function with given fields: block, txResponseCode -func (_m *BlockStore) SaveTxInfo(block *types.Block, txResponseCode []uint32) error { - ret := _m.Called(block, txResponseCode) +func (_m *BlockStore) SaveTxInfo(block *types.Block, txResponseCodes []uint32, logs []string) error { + ret := _m.Called(block, txResponseCodes, logs) var r0 error - if rf, ok := ret.Get(0).(func(*types.Block, []uint32) error); ok { - r0 = rf(block, txResponseCode) + if rf, ok := ret.Get(0).(func(*types.Block, []uint32, []string) error); ok { + r0 = rf(block, txResponseCodes, logs) } else { r0 = ret.Error(0) } diff --git a/state/services.go b/state/services.go index 829934105b..22fd741de1 100644 --- a/state/services.go +++ b/state/services.go @@ -26,7 +26,7 @@ type BlockStore interface { LoadBlock(height int64) *types.Block SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) - SaveTxInfo(block *types.Block, txResponseCode []uint32) error + SaveTxInfo(block *types.Block, txResponseCodes []uint32, logs []string) error PruneBlocks(height int64) (uint64, error) diff --git a/store/store.go b/store/store.go index f225f2a21a..00bb740906 100644 --- a/store/store.go +++ b/store/store.go @@ -7,6 +7,7 @@ import ( dbm "github.com/cometbft/cometbft-db" "github.com/gogo/protobuf/proto" + abci "github.com/tendermint/tendermint/abci/types" cmtsync "github.com/tendermint/tendermint/libs/sync" cmtstore "github.com/tendermint/tendermint/proto/tendermint/store" cmtproto "github.com/tendermint/tendermint/proto/tendermint/types" @@ -450,11 +451,15 @@ func (bs *BlockStore) SaveSeenCommit(height int64, seenCommit *types.Commit) err return bs.db.Set(calcSeenCommitKey(height), seenCommitBytes) } -// SaveTxInfo indexes the txs from the block with the given response codes from execution. -func (bs *BlockStore) SaveTxInfo(block *types.Block, txResponseCodes []uint32) error { +// SaveTxInfo indexes the txs from the block with the given response codes and logs from execution. +// The logs are only saved for failed transactions. +func (bs *BlockStore) SaveTxInfo(block *types.Block, txResponseCodes []uint32, logs []string) error { if len(txResponseCodes) != len(block.Txs) { return fmt.Errorf("txResponseCodes length mismatch with block txs length") } + if len(logs) != len(block.Txs) { + return fmt.Errorf("logs length mismatch with block txs length") + } // Create a new batch batch := bs.db.NewBatch() @@ -466,6 +471,10 @@ func (bs *BlockStore) SaveTxInfo(block *types.Block, txResponseCodes []uint32) e Index: uint32(i), Code: txResponseCodes[i], } + // Only save Logs for failed transactions + if txResponseCodes[i] != abci.CodeTypeOK { + txInfo.Log = logs[i] + } txInfoBytes, err := proto.Marshal(&txInfo) if err != nil { return fmt.Errorf("unable to marshal tx: %w", err) diff --git a/store/store_test.go b/store/store_test.go index cd80803d2c..a8e2c6e8e6 100644 --- a/store/store_test.go +++ b/store/store_test.go @@ -384,24 +384,27 @@ func TestSaveTxInfo(t *testing.T) { // Create 1000 blocks txResponseCodes := make([]uint32, len(block.Txs)) + logs := make([]string, len(block.Txs)) for h := int64(1); h <= 1000; h++ { block := makeBlock(h, state, new(types.Commit)) partSet := block.MakePartSet(2) seenCommit := makeTestCommit(h, cmttime.Now()) blockStore.SaveBlock(block, partSet, seenCommit) - // Set the response codes for the transactions + // Set the response codes and logs for the transactions for i := range block.Txs { // If even set it to 0 if i%2 == 0 { txResponseCodes[i] = 0 + logs[i] = "success" } else { txResponseCodes[i] = 1 + logs[i] = "failure" } } // Save the tx info - err := blockStore.SaveTxInfo(block, txResponseCodes) + err := blockStore.SaveTxInfo(block, txResponseCodes, logs) require.NoError(t, err) } @@ -414,6 +417,12 @@ func TestSaveTxInfo(t *testing.T) { require.Equal(t, block.Height, txInfo.Height) require.Equal(t, uint32(i), txInfo.Index) require.Equal(t, txResponseCodes[i], txInfo.Code) + // We don't save the logs for successful transactions + if txResponseCodes[i] == 0 { + require.Equal(t, "", txInfo.Log) + } else { + require.Equal(t, logs[i], txInfo.Log) + } } } @@ -426,6 +435,7 @@ func TestSaveTxInfo(t *testing.T) { require.Equal(t, txInfo.Height, int64(777)) require.Equal(t, uint32(1), txInfo.Code) require.Equal(t, uint32(5), txInfo.Index) + require.Equal(t, "failure", txInfo.Log) } func TestLoadBaseMeta(t *testing.T) { @@ -597,7 +607,7 @@ func TestPruneBlocksPrunesTxs(t *testing.T) { partSet := block.MakePartSet(2) seenCommit := makeTestCommit(h, cmttime.Now()) blockStore.SaveBlock(block, partSet, seenCommit) - err := blockStore.SaveTxInfo(block, make([]uint32, len(block.Txs))) + err := blockStore.SaveTxInfo(block, make([]uint32, len(block.Txs)), make([]string, len(block.Txs))) require.NoError(t, err) for _, tx := range block.Txs { indexedTxHashes = append(indexedTxHashes, tx.Hash())