erigon-pulse/turbo/snapshotsync/freezeblocks/dump_test.go
2024-01-09 19:20:42 +01:00

283 lines
8.6 KiB
Go

package freezeblocks_test
import (
"github.com/ledgerwatch/erigon/polygon/bor/borcfg"
"math/big"
"testing"
"github.com/holiman/uint256"
"github.com/ledgerwatch/erigon-lib/chain/networkname"
"github.com/ledgerwatch/erigon-lib/chain/snapcfg"
"github.com/ledgerwatch/log/v3"
"github.com/stretchr/testify/require"
"github.com/ledgerwatch/erigon-lib/chain"
libcommon "github.com/ledgerwatch/erigon-lib/common"
types2 "github.com/ledgerwatch/erigon-lib/types"
"github.com/ledgerwatch/erigon/common/math"
"github.com/ledgerwatch/erigon/core"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/crypto"
"github.com/ledgerwatch/erigon/ethdb/prune"
"github.com/ledgerwatch/erigon/params"
"github.com/ledgerwatch/erigon/rlp"
"github.com/ledgerwatch/erigon/turbo/snapshotsync/freezeblocks"
"github.com/ledgerwatch/erigon/turbo/stages/mock"
)
func nonceRange(from, to int) []uint64 {
a := make([]uint64, to-from+1)
for i := range a {
a[i] = uint64(from + i)
}
return a
}
func baseIdRange(base, indexer, len int) []uint64 {
a := make([]uint64, len)
index := 0
for i := range a {
a[i] = uint64(base + index)
index += indexer
}
return a
}
func TestDump(t *testing.T) {
type test struct {
chainConfig *chain.Config
chainSize int
}
withConfig := func(config chain.Config, sprints map[string]uint64) *chain.Config {
bor := *config.Bor.(*borcfg.BorConfig)
bor.Sprint = sprints
config.Bor = &bor
return &config
}
tests := []test{
{
chainSize: 5,
chainConfig: params.TestChainConfig,
},
{
chainSize: 50,
chainConfig: params.TestChainConfig,
},
{
chainSize: 1000,
chainConfig: params.BorDevnetChainConfig,
},
{
chainSize: 2000,
chainConfig: params.BorDevnetChainConfig,
},
{
chainSize: 1000,
chainConfig: withConfig(*params.BorDevnetChainConfig,
map[string]uint64{
"0": 64,
"800": 16,
"1600": 8,
}),
},
{
chainSize: 2000,
chainConfig: withConfig(*params.BorDevnetChainConfig,
map[string]uint64{
"0": 64,
"800": 16,
"1600": 8,
}),
},
}
for _, test := range tests {
m := createDumpTestKV(t, test.chainConfig, test.chainSize)
chainID, _ := uint256.FromBig(m.ChainConfig.ChainID)
t.Run("txs", func(t *testing.T) {
require := require.New(t)
slot := types2.TxSlot{}
parseCtx := types2.NewTxParseContext(*chainID)
parseCtx.WithSender(false)
var sender [20]byte
var systemTxs int
var nonceList []uint64
cnt, err := freezeblocks.DumpTxs(m.Ctx, m.DB, 0, uint64(2*test.chainSize), m.ChainConfig, 1, log.LvlInfo, log.New(), func(v []byte) error {
if v == nil {
systemTxs++
} else {
if _, err := parseCtx.ParseTransaction(v[1+20:], 0, &slot, sender[:], false /* hasEnvelope */, false /* wrappedWithBlobs */, nil); err != nil {
return err
}
nonceList = append(nonceList, slot.Nonce)
}
return nil
})
require.NoError(err)
require.Equal(2*(test.chainSize+1), systemTxs)
require.Equal(nonceRange(0, test.chainSize-1), nonceList)
require.Equal(2*(test.chainSize+1)+test.chainSize, cnt)
})
t.Run("txs_not_from_zero", func(t *testing.T) {
require := require.New(t)
slot := types2.TxSlot{}
parseCtx := types2.NewTxParseContext(*chainID)
parseCtx.WithSender(false)
var sender [20]byte
var systemTxs int
var nonceList []uint64
cnt, err := freezeblocks.DumpTxs(m.Ctx, m.DB, 2, uint64(test.chainSize), m.ChainConfig, 1, log.LvlInfo, log.New(), func(v []byte) error {
if v == nil {
systemTxs++
} else {
if _, err := parseCtx.ParseTransaction(v[1+20:], 0, &slot, sender[:], false /* hasEnvelope */, false /* wrappedWithBlobs */, nil); err != nil {
return err
}
nonceList = append(nonceList, slot.Nonce)
}
return nil
})
require.NoError(err)
require.Equal(2*(test.chainSize-2), systemTxs)
require.Equal(nonceRange(1, test.chainSize-2), nonceList)
require.Equal(3*test.chainSize-6, cnt)
})
t.Run("headers", func(t *testing.T) {
require := require.New(t)
var nonceList []uint64
err := freezeblocks.DumpHeaders(m.Ctx, m.DB, 0, uint64(2*test.chainSize), 1, log.LvlInfo, log.New(), func(v []byte) error {
h := types.Header{}
if err := rlp.DecodeBytes(v[1:], &h); err != nil {
return err
}
nonceList = append(nonceList, h.Number.Uint64())
return nil
})
require.NoError(err)
require.Equal(nonceRange(0, test.chainSize), nonceList)
})
t.Run("headers_not_from_zero", func(t *testing.T) {
require := require.New(t)
var nonceList []uint64
err := freezeblocks.DumpHeaders(m.Ctx, m.DB, 2, uint64(test.chainSize), 1, log.LvlInfo, log.New(), func(v []byte) error {
h := types.Header{}
if err := rlp.DecodeBytes(v[1:], &h); err != nil {
return err
}
nonceList = append(nonceList, h.Number.Uint64())
return nil
})
require.NoError(err)
require.Equal(nonceRange(2, test.chainSize-1), nonceList)
})
t.Run("body", func(t *testing.T) {
require := require.New(t)
i := 0
txsAmount := uint64(0)
var baseIdList []uint64
firstTxNum := uint64(0)
_, err := freezeblocks.DumpBodies(m.Ctx, m.DB, 0, uint64(test.chainSize-3), firstTxNum, log.LvlInfo, log.New(), func(v []byte) error {
i++
body := &types.BodyForStorage{}
require.NoError(rlp.DecodeBytes(v, body))
txsAmount += uint64(body.TxAmount)
baseIdList = append(baseIdList, body.BaseTxId)
return nil
})
require.NoError(err)
require.Equal(test.chainSize-3, i)
require.Equal(3*(test.chainSize-3)-1, int(txsAmount))
require.Equal(append([]uint64{0}, baseIdRange(2, 3, test.chainSize-4)...), baseIdList)
firstTxNum += txsAmount
i = 0
baseIdList = baseIdList[:0]
_, err = freezeblocks.DumpBodies(m.Ctx, m.DB, 2, uint64(2*test.chainSize), firstTxNum, log.LvlInfo, log.New(), func(v []byte) error {
i++
body := &types.BodyForStorage{}
require.NoError(rlp.DecodeBytes(v, body))
txsAmount += uint64(body.TxAmount)
baseIdList = append(baseIdList, body.BaseTxId)
return nil
})
require.NoError(err)
require.Equal(test.chainSize-1, i)
require.Equal(firstTxNum+uint64(3*(test.chainSize-1)), txsAmount)
require.Equal(baseIdRange(int(firstTxNum), 3, test.chainSize-1), baseIdList)
})
t.Run("body_not_from_zero", func(t *testing.T) {
require := require.New(t)
i := 0
var baseIdList []uint64
firstTxNum := uint64(1000)
lastTxNum, err := freezeblocks.DumpBodies(m.Ctx, m.DB, 2, uint64(test.chainSize), firstTxNum, log.LvlInfo, log.New(), func(v []byte) error {
i++
body := &types.BodyForStorage{}
require.NoError(rlp.DecodeBytes(v, body))
baseIdList = append(baseIdList, body.BaseTxId)
return nil
})
require.NoError(err)
require.Equal(test.chainSize-2, i)
require.Equal(baseIdRange(int(firstTxNum), 3, test.chainSize-2), baseIdList)
require.Equal(lastTxNum, baseIdList[len(baseIdList)-1]+3)
require.Equal(lastTxNum, firstTxNum+uint64(i*3))
})
t.Run("blocks", func(t *testing.T) {
if test.chainSize < 1000 || test.chainSize%1000 != 0 {
t.Skip("Block dump requires chain size to be a multiple of 1000")
}
require := require.New(t)
logger := log.New()
tmpDir, snapDir := t.TempDir(), t.TempDir()
snConfig := snapcfg.KnownCfg(networkname.MainnetChainName, 0)
snConfig.ExpectBlocks = math.MaxUint64
err := freezeblocks.DumpBlocks(m.Ctx, 1, 0, uint64(test.chainSize), uint64(test.chainSize), tmpDir, snapDir, m.DB, 1, log.LvlInfo, logger, m.BlockReader)
require.NoError(err)
})
}
}
func createDumpTestKV(t *testing.T, chainConfig *chain.Config, chainSize int) *mock.MockSentry {
var (
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
addr = crypto.PubkeyToAddress(key.PublicKey)
gspec = &types.Genesis{
Config: chainConfig,
Alloc: types.GenesisAlloc{addr: {Balance: (&big.Int{}).Mul(big.NewInt(math.MaxInt64), big.NewInt(int64(chainSize)))}},
}
signer = types.LatestSigner(gspec.Config)
)
m := mock.MockWithGenesisPruneMode(t, gspec, key, chainSize, prune.DefaultMode, false)
// Generate testing blocks
chain, err := core.GenerateChain(m.ChainConfig, m.Genesis, m.Engine, m.DB, chainSize, func(i int, b *core.BlockGen) {
b.SetCoinbase(libcommon.Address{1})
tx, txErr := types.SignTx(types.NewTransaction(b.TxNonce(addr), libcommon.HexToAddress("deadbeef"), uint256.NewInt(100), 21000, uint256.NewInt(uint64(int64(i+1)*params.GWei)), nil), *signer, key)
if txErr != nil {
t.Fatalf("failed to create tx: %v", txErr)
}
b.AddTx(tx)
})
if err != nil {
t.Fatal(err)
}
// Construct testing chain
if err = m.InsertChain(chain); err != nil {
t.Fatal(err)
}
return m
}