// Copyright (c) 2017-2026 Lethean (https://lt.hn) // // Licensed under the European Union Public Licence (EUPL) version 1.2. // SPDX-License-Identifier: EUPL-1.2 package chain import ( "testing" "dappco.re/go/core/blockchain/types" "github.com/stretchr/testify/require" ) func TestSparseChainHistory_Empty(t *testing.T) { c := newTestChain(t) history, err := c.SparseChainHistory() require.NoError(t, err) require.Len(t, history, 1) // genesis hash so the peer can locate us expected, _ := types.HashFromHex(GenesisHash) require.Equal(t, expected, history[0]) } func TestSparseChainHistory_FewBlocks(t *testing.T) { c := newTestChain(t) // Store 5 blocks with known hashes. for i := uint64(0); i < 5; i++ { hash := types.Hash{byte(i + 1)} blk := &types.Block{ BlockHeader: types.BlockHeader{MajorVersion: 1}, MinerTx: testCoinbaseTx(i), } if i > 0 { blk.PrevID = types.Hash{byte(i)} } err := c.PutBlock(blk, &BlockMeta{Hash: hash, Height: i}) require.NoError(t, err) } history, err := c.SparseChainHistory() require.NoError(t, err) // With 5 blocks (heights 0-4), all within the first 10 so step=1 // throughout. Should return hashes for heights 4, 3, 2, 1, 0. require.Greater(t, len(history), 0) // First entry should be top block hash (height 4). require.Equal(t, types.Hash{5}, history[0]) // Last entry should be genesis hash (height 0). require.Equal(t, types.Hash{1}, history[len(history)-1]) // All 5 blocks should be present since count < 10. require.Len(t, history, 5) } func TestSparseChainHistory_SingleBlock(t *testing.T) { c := newTestChain(t) blk := &types.Block{ BlockHeader: types.BlockHeader{MajorVersion: 1}, MinerTx: testCoinbaseTx(0), } err := c.PutBlock(blk, &BlockMeta{Hash: types.Hash{0xaa}, Height: 0}) require.NoError(t, err) history, err := c.SparseChainHistory() require.NoError(t, err) require.Len(t, history, 1) require.Equal(t, types.Hash{0xaa}, history[0]) } func TestSparseChainHistory_ExponentialSpacing(t *testing.T) { c := newTestChain(t) // Store 20 blocks (heights 0-19) to exercise exponential stepping. for i := uint64(0); i < 20; i++ { hash := types.Hash{byte(i + 1)} blk := &types.Block{ BlockHeader: types.BlockHeader{MajorVersion: 1}, MinerTx: testCoinbaseTx(i), } if i > 0 { blk.PrevID = types.Hash{byte(i)} } err := c.PutBlock(blk, &BlockMeta{Hash: hash, Height: i}) require.NoError(t, err) } history, err := c.SparseChainHistory() require.NoError(t, err) // First entry should be top block (height 19). require.Equal(t, types.Hash{20}, history[0]) // Last entry should be genesis (height 0). require.Equal(t, types.Hash{1}, history[len(history)-1]) // Should have fewer entries than total blocks due to exponential steps. require.Less(t, len(history), 20) // First 10 entries should be consecutive (step=1): heights 19, 18, ..., 10. for i := 0; i < 10; i++ { expected := types.Hash{byte(20 - i)} require.Equal(t, expected, history[i], "entry %d: expected hash for height %d", i, 19-i) } // After the first 10, steps double each entry: // Entry 10: step becomes 2, current = 10 - 2 = 8 -> hash {9} // Entry 11: step becomes 4, current = 8 - 4 = 4 -> hash {5} // Entry 12: step becomes 8, current 4 < 8, jump to 0 -> hash {1} (genesis) require.Equal(t, types.Hash{byte(9)}, history[10]) // height 8 require.Equal(t, types.Hash{byte(5)}, history[11]) // height 4 require.Equal(t, types.Hash{byte(1)}, history[12]) // height 0 (genesis) require.Len(t, history, 13) }