From 25b267f04da8aaa6d43f7f0af84b49be5409816a Mon Sep 17 00:00:00 2001 From: kuba-4chain Date: Thu, 5 Dec 2024 13:21:40 +0100 Subject: [PATCH] feat: include height range in getting the chain tip --- internal/blocktx/processor.go | 10 +++- internal/blocktx/processor_test.go | 10 ++-- .../blocktx/store/mocks/blocktx_store_mock.go | 52 +++++++++---------- .../get_block_by_height/blocktx.blocks.yaml | 12 +++++ .../blocktx.blocks.yaml | 2 +- .../blocktx/store/postgresql/get_block.go | 10 ++-- .../store/postgresql/get_transactions.go | 9 +--- .../blocktx/store/postgresql/postgres_test.go | 26 +++++----- internal/blocktx/store/store.go | 4 +- 9 files changed, 77 insertions(+), 58 deletions(-) diff --git a/internal/blocktx/processor.go b/internal/blocktx/processor.go index 2683f368f..da6b641bb 100644 --- a/internal/blocktx/processor.go +++ b/internal/blocktx/processor.go @@ -397,7 +397,7 @@ func (p *Processor) publishMinedTxs(txHashes []*chainhash.Hash) error { hashesBytes[i] = h[:] } - minedTxs, err := p.store.GetMinedTransactions(p.ctx, hashesBytes, false) + minedTxs, err := p.store.GetMinedTransactions(p.ctx, hashesBytes) if err != nil { return fmt.Errorf("failed to get mined transactions: %v", err) } @@ -594,7 +594,13 @@ func (p *Processor) assignBlockStatus(ctx context.Context, block *blocktx_api.Bl } func (p *Processor) longestTipExists(ctx context.Context) (bool, error) { - _, err := p.store.GetChainTip(ctx) + const ( + hoursPerDay = 24 + blocksPerHour = 6 + ) + heightRange := p.dataRetentionDays * hoursPerDay * blocksPerHour + + _, err := p.store.GetChainTip(ctx, heightRange) if err != nil && !errors.Is(err, store.ErrBlockNotFound) { return false, err } diff --git a/internal/blocktx/processor_test.go b/internal/blocktx/processor_test.go index dc531767b..6d0589849 100644 --- a/internal/blocktx/processor_test.go +++ b/internal/blocktx/processor_test.go @@ -166,13 +166,13 @@ func TestHandleBlock(t *testing.T) { GetLongestBlockByHeightFunc: func(_ context.Context, _ uint64) (*blocktx_api.Block, error) { return nil, store.ErrBlockNotFound }, - GetChainTipFunc: func(_ context.Context) (*blocktx_api.Block, error) { + GetChainTipFunc: func(_ context.Context, _ int) (*blocktx_api.Block, error) { return nil, store.ErrBlockNotFound }, UpsertBlockFunc: func(_ context.Context, _ *blocktx_api.Block) (uint64, error) { return 0, nil }, - GetMinedTransactionsFunc: func(_ context.Context, _ [][]byte, _ bool) ([]store.TransactionBlock, error) { + GetMinedTransactionsFunc: func(_ context.Context, _ [][]byte) ([]store.TransactionBlock, error) { return nil, nil }, GetRegisteredTxsByBlockHashesFunc: func(_ context.Context, _ [][]byte) ([]store.TransactionBlock, error) { @@ -354,7 +354,7 @@ func TestHandleBlockReorgAndOrphans(t *testing.T) { } return nil, store.ErrBlockNotFound }, - GetChainTipFunc: func(_ context.Context) (*blocktx_api.Block, error) { + GetChainTipFunc: func(_ context.Context, _ int) (*blocktx_api.Block, error) { return &blocktx_api.Block{}, nil }, UpsertBlockFunc: func(_ context.Context, block *blocktx_api.Block) (uint64, error) { @@ -414,7 +414,7 @@ func TestHandleBlockReorgAndOrphans(t *testing.T) { GetRegisteredTxsByBlockHashesFunc: func(_ context.Context, _ [][]byte) ([]store.TransactionBlock, error) { return nil, nil }, - GetMinedTransactionsFunc: func(_ context.Context, _ [][]byte, _ bool) ([]store.TransactionBlock, error) { + GetMinedTransactionsFunc: func(_ context.Context, _ [][]byte) ([]store.TransactionBlock, error) { return nil, nil }, MarkBlockAsDoneFunc: func(_ context.Context, _ *chainhash.Hash, _, _ uint64) error { @@ -710,7 +710,7 @@ func TestStartProcessRequestTxs(t *testing.T) { t.Run(tc.name, func(t *testing.T) { // given storeMock := &storeMocks.BlocktxStoreMock{ - GetMinedTransactionsFunc: func(_ context.Context, hashes [][]byte, _ bool) ([]store.TransactionBlock, error) { + GetMinedTransactionsFunc: func(_ context.Context, hashes [][]byte) ([]store.TransactionBlock, error) { for _, hash := range hashes { require.Equal(t, testdata.TX1Hash[:], hash) } diff --git a/internal/blocktx/store/mocks/blocktx_store_mock.go b/internal/blocktx/store/mocks/blocktx_store_mock.go index d7acfb51d..6cc42a265 100644 --- a/internal/blocktx/store/mocks/blocktx_store_mock.go +++ b/internal/blocktx/store/mocks/blocktx_store_mock.go @@ -39,7 +39,7 @@ var _ store.BlocktxStore = &BlocktxStoreMock{} // GetBlockHashesProcessingInProgressFunc: func(ctx context.Context, processedBy string) ([]*chainhash.Hash, error) { // panic("mock out the GetBlockHashesProcessingInProgress method") // }, -// GetChainTipFunc: func(ctx context.Context) (*blocktx_api.Block, error) { +// GetChainTipFunc: func(ctx context.Context, heightRange int) (*blocktx_api.Block, error) { // panic("mock out the GetChainTip method") // }, // GetLongestBlockByHeightFunc: func(ctx context.Context, height uint64) (*blocktx_api.Block, error) { @@ -48,7 +48,7 @@ var _ store.BlocktxStore = &BlocktxStoreMock{} // GetLongestChainFromHeightFunc: func(ctx context.Context, height uint64) ([]*blocktx_api.Block, error) { // panic("mock out the GetLongestChainFromHeight method") // }, -// GetMinedTransactionsFunc: func(ctx context.Context, hashes [][]byte, onlyLongestChain bool) ([]store.TransactionBlock, error) { +// GetMinedTransactionsFunc: func(ctx context.Context, hashes [][]byte) ([]store.TransactionBlock, error) { // panic("mock out the GetMinedTransactions method") // }, // GetOrphansBackToNonOrphanAncestorFunc: func(ctx context.Context, hash []byte) ([]*blocktx_api.Block, *blocktx_api.Block, error) { @@ -113,7 +113,7 @@ type BlocktxStoreMock struct { GetBlockHashesProcessingInProgressFunc func(ctx context.Context, processedBy string) ([]*chainhash.Hash, error) // GetChainTipFunc mocks the GetChainTip method. - GetChainTipFunc func(ctx context.Context) (*blocktx_api.Block, error) + GetChainTipFunc func(ctx context.Context, heightRange int) (*blocktx_api.Block, error) // GetLongestBlockByHeightFunc mocks the GetLongestBlockByHeight method. GetLongestBlockByHeightFunc func(ctx context.Context, height uint64) (*blocktx_api.Block, error) @@ -122,7 +122,7 @@ type BlocktxStoreMock struct { GetLongestChainFromHeightFunc func(ctx context.Context, height uint64) ([]*blocktx_api.Block, error) // GetMinedTransactionsFunc mocks the GetMinedTransactions method. - GetMinedTransactionsFunc func(ctx context.Context, hashes [][]byte, onlyLongestChain bool) ([]store.TransactionBlock, error) + GetMinedTransactionsFunc func(ctx context.Context, hashes [][]byte) ([]store.TransactionBlock, error) // GetOrphansBackToNonOrphanAncestorFunc mocks the GetOrphansBackToNonOrphanAncestor method. GetOrphansBackToNonOrphanAncestorFunc func(ctx context.Context, hash []byte) ([]*blocktx_api.Block, *blocktx_api.Block, error) @@ -208,6 +208,8 @@ type BlocktxStoreMock struct { GetChainTip []struct { // Ctx is the ctx argument value. Ctx context.Context + // HeightRange is the heightRange argument value. + HeightRange int } // GetLongestBlockByHeight holds details about calls to the GetLongestBlockByHeight method. GetLongestBlockByHeight []struct { @@ -229,8 +231,6 @@ type BlocktxStoreMock struct { Ctx context.Context // Hashes is the hashes argument value. Hashes [][]byte - // OnlyLongestChain is the onlyLongestChain argument value. - OnlyLongestChain bool } // GetOrphansBackToNonOrphanAncestor holds details about calls to the GetOrphansBackToNonOrphanAncestor method. GetOrphansBackToNonOrphanAncestor []struct { @@ -563,19 +563,21 @@ func (mock *BlocktxStoreMock) GetBlockHashesProcessingInProgressCalls() []struct } // GetChainTip calls GetChainTipFunc. -func (mock *BlocktxStoreMock) GetChainTip(ctx context.Context) (*blocktx_api.Block, error) { +func (mock *BlocktxStoreMock) GetChainTip(ctx context.Context, heightRange int) (*blocktx_api.Block, error) { if mock.GetChainTipFunc == nil { panic("BlocktxStoreMock.GetChainTipFunc: method is nil but BlocktxStore.GetChainTip was just called") } callInfo := struct { - Ctx context.Context + Ctx context.Context + HeightRange int }{ - Ctx: ctx, + Ctx: ctx, + HeightRange: heightRange, } mock.lockGetChainTip.Lock() mock.calls.GetChainTip = append(mock.calls.GetChainTip, callInfo) mock.lockGetChainTip.Unlock() - return mock.GetChainTipFunc(ctx) + return mock.GetChainTipFunc(ctx, heightRange) } // GetChainTipCalls gets all the calls that were made to GetChainTip. @@ -583,10 +585,12 @@ func (mock *BlocktxStoreMock) GetChainTip(ctx context.Context) (*blocktx_api.Blo // // len(mockedBlocktxStore.GetChainTipCalls()) func (mock *BlocktxStoreMock) GetChainTipCalls() []struct { - Ctx context.Context + Ctx context.Context + HeightRange int } { var calls []struct { - Ctx context.Context + Ctx context.Context + HeightRange int } mock.lockGetChainTip.RLock() calls = mock.calls.GetChainTip @@ -667,23 +671,21 @@ func (mock *BlocktxStoreMock) GetLongestChainFromHeightCalls() []struct { } // GetMinedTransactions calls GetMinedTransactionsFunc. -func (mock *BlocktxStoreMock) GetMinedTransactions(ctx context.Context, hashes [][]byte, onlyLongestChain bool) ([]store.TransactionBlock, error) { +func (mock *BlocktxStoreMock) GetMinedTransactions(ctx context.Context, hashes [][]byte) ([]store.TransactionBlock, error) { if mock.GetMinedTransactionsFunc == nil { panic("BlocktxStoreMock.GetMinedTransactionsFunc: method is nil but BlocktxStore.GetMinedTransactions was just called") } callInfo := struct { - Ctx context.Context - Hashes [][]byte - OnlyLongestChain bool + Ctx context.Context + Hashes [][]byte }{ - Ctx: ctx, - Hashes: hashes, - OnlyLongestChain: onlyLongestChain, + Ctx: ctx, + Hashes: hashes, } mock.lockGetMinedTransactions.Lock() mock.calls.GetMinedTransactions = append(mock.calls.GetMinedTransactions, callInfo) mock.lockGetMinedTransactions.Unlock() - return mock.GetMinedTransactionsFunc(ctx, hashes, onlyLongestChain) + return mock.GetMinedTransactionsFunc(ctx, hashes) } // GetMinedTransactionsCalls gets all the calls that were made to GetMinedTransactions. @@ -691,14 +693,12 @@ func (mock *BlocktxStoreMock) GetMinedTransactions(ctx context.Context, hashes [ // // len(mockedBlocktxStore.GetMinedTransactionsCalls()) func (mock *BlocktxStoreMock) GetMinedTransactionsCalls() []struct { - Ctx context.Context - Hashes [][]byte - OnlyLongestChain bool + Ctx context.Context + Hashes [][]byte } { var calls []struct { - Ctx context.Context - Hashes [][]byte - OnlyLongestChain bool + Ctx context.Context + Hashes [][]byte } mock.lockGetMinedTransactions.RLock() calls = mock.calls.GetMinedTransactions diff --git a/internal/blocktx/store/postgresql/fixtures/get_block_by_height/blocktx.blocks.yaml b/internal/blocktx/store/postgresql/fixtures/get_block_by_height/blocktx.blocks.yaml index c1a8f0b30..f6018101b 100644 --- a/internal/blocktx/store/postgresql/fixtures/get_block_by_height/blocktx.blocks.yaml +++ b/internal/blocktx/store/postgresql/fixtures/get_block_by_height/blocktx.blocks.yaml @@ -54,3 +54,15 @@ tx_count: 36724 status: 10 chainwork: '123456' +- inserted_at: 2023-12-15 14:50:00 + id: 5 + hash: 0x000000000000000005167c951069b0e3c803753b8ebeaa2ddaca85b89526b297 + prevhash: 0x76404890880cb36ce68100abb05b3a958e17c0ed274d5c0a0000000000000000 + merkleroot: 0xc458aa382364e216c9c0533175ec8579a544c750ca181b18296e784d1dc53085 + height: 822025 + processed_at: 2023-12-15 14:40:00 + size: 8630000 + tx_count: 36724 + status: 30 # ORPHANED + is_longest: false + chainwork: '123456' diff --git a/internal/blocktx/store/postgresql/fixtures/upsert_block_transactions/blocktx.blocks.yaml b/internal/blocktx/store/postgresql/fixtures/upsert_block_transactions/blocktx.blocks.yaml index 59e5f2fbe..8c4d7ec35 100644 --- a/internal/blocktx/store/postgresql/fixtures/upsert_block_transactions/blocktx.blocks.yaml +++ b/internal/blocktx/store/postgresql/fixtures/upsert_block_transactions/blocktx.blocks.yaml @@ -4,7 +4,7 @@ prevhash: 0x000000000000000001a7aa3999410ca53fb645851531ec0a7a5cb9ce2d4ae313 merkleroot: 0x0d72bf92e7862df18d1935c171ca4dbb70d268b0f025e46716e913bc7e4f2bdb height: 826481 - status: 10 # STALE + status: 10 is_longest: true processed_at: 2024-01-10 13:06:06.122 size: 108689370 diff --git a/internal/blocktx/store/postgresql/get_block.go b/internal/blocktx/store/postgresql/get_block.go index adfaadb39..edac0b449 100644 --- a/internal/blocktx/store/postgresql/get_block.go +++ b/internal/blocktx/store/postgresql/get_block.go @@ -22,10 +22,14 @@ func (p *PostgreSQL) GetLongestBlockByHeight(ctx context.Context, height uint64) return p.queryBlockByPredicate(ctx, predicate, height) } -func (p *PostgreSQL) GetChainTip(ctx context.Context) (*blocktx_api.Block, error) { - predicate := "WHERE height = (SELECT MAX(height) FROM blocktx.blocks blks WHERE blks.is_longest = true)" +func (p *PostgreSQL) GetChainTip(ctx context.Context, heightRange int) (*blocktx_api.Block, error) { + predicate := `WHERE height = (SELECT MAX(height) from blocktx.blocks WHERE is_longest = true) + AND is_longest = true + AND height > (SELECT MAX(height) - $1 from blocktx.blocks) + AND processed_at IS NOT NULL + ` - return p.queryBlockByPredicate(ctx, predicate) + return p.queryBlockByPredicate(ctx, predicate, heightRange) } func (p *PostgreSQL) queryBlockByPredicate(ctx context.Context, predicate string, predicateParams ...any) (*blocktx_api.Block, error) { diff --git a/internal/blocktx/store/postgresql/get_transactions.go b/internal/blocktx/store/postgresql/get_transactions.go index 5900cf308..bb4dddfda 100644 --- a/internal/blocktx/store/postgresql/get_transactions.go +++ b/internal/blocktx/store/postgresql/get_transactions.go @@ -9,17 +9,12 @@ import ( "github.com/lib/pq" ) -func (p *PostgreSQL) GetMinedTransactions(ctx context.Context, hashes [][]byte, onlyLongestChain bool) (minedTransactions []store.TransactionBlock, err error) { +func (p *PostgreSQL) GetMinedTransactions(ctx context.Context, hashes [][]byte) (minedTransactions []store.TransactionBlock, err error) { ctx, span := tracing.StartTracing(ctx, "GetMinedTransactions", p.tracingEnabled, p.tracingAttributes...) defer func() { tracing.EndTracing(span, err) }() - if onlyLongestChain { - predicate := "WHERE t.hash = ANY($1) AND b.is_longest = true" - return p.getTransactionBlocksByPredicate(ctx, predicate, pq.Array(hashes)) - } - predicate := "WHERE t.hash = ANY($1) AND (b.status = $2 OR b.status = $3) AND b.processed_at IS NOT NULL" return p.getTransactionBlocksByPredicate(ctx, predicate, @@ -30,7 +25,7 @@ func (p *PostgreSQL) GetMinedTransactions(ctx context.Context, hashes [][]byte, } func (p *PostgreSQL) GetRegisteredTxsByBlockHashes(ctx context.Context, blockHashes [][]byte) (registeredTxs []store.TransactionBlock, err error) { - ctx, span := tracing.StartTracing(ctx, "GetMinedTransactions", p.tracingEnabled, p.tracingAttributes...) + ctx, span := tracing.StartTracing(ctx, "GetRegisteredTxsByBlockHashes", p.tracingEnabled, p.tracingAttributes...) defer func() { tracing.EndTracing(span, err) }() diff --git a/internal/blocktx/store/postgresql/postgres_test.go b/internal/blocktx/store/postgresql/postgres_test.go index 13d686959..25b4fa84a 100644 --- a/internal/blocktx/store/postgresql/postgres_test.go +++ b/internal/blocktx/store/postgresql/postgres_test.go @@ -213,10 +213,14 @@ func TestPostgresDB(t *testing.T) { require.Nil(t, actualBlock) require.Equal(t, store.ErrBlockNotFound, err) - actualBlock, err = postgresDB.GetChainTip(context.Background()) + actualBlock, err = postgresDB.GetChainTip(context.Background(), 10) require.NoError(t, err) require.Equal(t, hashAtTip[:], actualBlock.Hash) require.Equal(t, expectedTipHeight, actualBlock.Height) + + actualBlock, err = postgresDB.GetChainTip(context.Background(), 2) + require.Nil(t, actualBlock) + require.Equal(t, store.ErrBlockNotFound, err) }) t.Run("get block gaps", func(t *testing.T) { @@ -453,16 +457,7 @@ func TestPostgresDB(t *testing.T) { } // when - onlyLongestChain := true - actualTxs, err := postgresDB.GetMinedTransactions(ctx, [][]byte{txHash1[:], txHash2[:], txHash3[:]}, onlyLongestChain) - - // then - require.NoError(t, err) - require.ElementsMatch(t, expectedTxs[:2], actualTxs) - - // when - onlyLongestChain = false - actualTxs, err = postgresDB.GetMinedTransactions(ctx, [][]byte{txHash1[:], txHash2[:], txHash3[:]}, onlyLongestChain) + actualTxs, err := postgresDB.GetMinedTransactions(ctx, [][]byte{txHash1[:], txHash2[:], txHash3[:]}) // then require.NoError(t, err) @@ -828,6 +823,13 @@ func TestPostgresStore_UpsertBlockTransactions_CompetingBlocks(t *testing.T) { MerklePath: "merkle-path-1", BlockStatus: blocktx_api.Status_LONGEST, }, + { + TxHash: txHash[:], + BlockHash: testutils.RevChainhash(t, "7258b02da70a3e367e4c993b049fa9b76ef8f090ef9fd2010000000000000000")[:], + BlockHeight: uint64(826481), + MerklePath: "merkle-path-2", + BlockStatus: blocktx_api.Status_STALE, + }, } // when @@ -838,7 +840,7 @@ func TestPostgresStore_UpsertBlockTransactions_CompetingBlocks(t *testing.T) { require.NoError(t, err) // then - actual, err := sut.GetMinedTransactions(ctx, [][]byte{txHash[:]}, true) + actual, err := sut.GetMinedTransactions(ctx, [][]byte{txHash[:]}) require.NoError(t, err) require.ElementsMatch(t, expected, actual) diff --git a/internal/blocktx/store/store.go b/internal/blocktx/store/store.go index 198bedac1..b601d61b0 100644 --- a/internal/blocktx/store/store.go +++ b/internal/blocktx/store/store.go @@ -32,13 +32,13 @@ type BlocktxStore interface { RegisterTransactions(ctx context.Context, txHashes [][]byte) (updatedTxs []*chainhash.Hash, err error) GetBlock(ctx context.Context, hash *chainhash.Hash) (*blocktx_api.Block, error) GetLongestBlockByHeight(ctx context.Context, height uint64) (*blocktx_api.Block, error) - GetChainTip(ctx context.Context) (*blocktx_api.Block, error) + GetChainTip(ctx context.Context, heightRange int) (*blocktx_api.Block, error) UpsertBlock(ctx context.Context, block *blocktx_api.Block) (uint64, error) UpsertBlockTransactions(ctx context.Context, blockID uint64, txsWithMerklePaths []TxWithMerklePath) error MarkBlockAsDone(ctx context.Context, hash *chainhash.Hash, size uint64, txCount uint64) error GetBlockGaps(ctx context.Context, heightRange int) ([]*BlockGap, error) ClearBlocktxTable(ctx context.Context, retentionDays int32, table string) (*blocktx_api.RowsAffectedResponse, error) - GetMinedTransactions(ctx context.Context, hashes [][]byte, onlyLongestChain bool) ([]TransactionBlock, error) + GetMinedTransactions(ctx context.Context, hashes [][]byte) ([]TransactionBlock, error) GetLongestChainFromHeight(ctx context.Context, height uint64) ([]*blocktx_api.Block, error) GetStaleChainBackFromHash(ctx context.Context, hash []byte) ([]*blocktx_api.Block, error) GetOrphansBackToNonOrphanAncestor(ctx context.Context, hash []byte) (orphans []*blocktx_api.Block, nonOrphanAncestor *blocktx_api.Block, err error)