internal/era: remove dependency on blockchain in era tests

This commit is contained in:
lightclient 2025-04-03 09:04:56 -06:00
parent e696f44b21
commit 48248e68f8
No known key found for this signature in database
GPG key ID: 657913021EF45A6A

View file

@ -18,20 +18,15 @@ package era
import ( import (
"bytes" "bytes"
"fmt"
"io" "io"
"math/big" "math/big"
"os" "os"
"path/filepath"
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/stretchr/testify/require"
) )
type testchain struct { type testchain struct {
@ -56,9 +51,9 @@ func TestEra1Builder(t *testing.T) {
chain = testchain{} chain = testchain{}
) )
for i := 0; i < 128; i++ { for i := 0; i < 128; i++ {
chain.headers = append(chain.headers, []byte{byte('h'), byte(i)}) chain.headers = append(chain.headers, mustEncode(&types.Header{Number: big.NewInt(int64(i))}))
chain.bodies = append(chain.bodies, []byte{byte('b'), byte(i)}) chain.bodies = append(chain.bodies, mustEncode(&types.Body{Transactions: []*types.Transaction{types.NewTransaction(0, common.Address{byte(i)}, nil, 0, nil, nil)}}))
chain.receipts = append(chain.receipts, []byte{byte('r'), byte(i)}) chain.receipts = append(chain.receipts, mustEncode(&types.Receipts{{CumulativeGasUsed: uint64(i)}}))
chain.tds = append(chain.tds, big.NewInt(int64(i))) chain.tds = append(chain.tds, big.NewInt(int64(i)))
} }
@ -99,13 +94,25 @@ func TestEra1Builder(t *testing.T) {
t.Fatalf("unexpected error %v", it.Error()) t.Fatalf("unexpected error %v", it.Error())
} }
// Check headers. // Check headers.
header, err := io.ReadAll(it.Header) rawHeader, err := io.ReadAll(it.Header)
if err != nil {
t.Fatalf("error reading header from iterator: %v", err)
}
if !bytes.Equal(rawHeader, chain.headers[i]) {
t.Fatalf("mismatched header: want %s, got %s", chain.headers[i], rawHeader)
}
header, err := e.GetHeaderByNumber(i)
if err != nil { if err != nil {
t.Fatalf("error reading header: %v", err) t.Fatalf("error reading header: %v", err)
} }
if !bytes.Equal(header, chain.headers[i]) { encHeader, err := rlp.EncodeToBytes(header)
t.Fatalf("mismatched header: want %s, got %s", chain.headers[i], header) if err != nil {
t.Fatalf("error encoding header: %v", err)
} }
if !bytes.Equal(encHeader, chain.headers[i]) {
t.Fatalf("mismatched header: want %s, got %s", chain.headers[i], encHeader)
}
// Check bodies. // Check bodies.
body, err := io.ReadAll(it.Body) body, err := io.ReadAll(it.Body)
if err != nil { if err != nil {
@ -114,13 +121,25 @@ func TestEra1Builder(t *testing.T) {
if !bytes.Equal(body, chain.bodies[i]) { if !bytes.Equal(body, chain.bodies[i]) {
t.Fatalf("mismatched body: want %s, got %s", chain.bodies[i], body) t.Fatalf("mismatched body: want %s, got %s", chain.bodies[i], body)
} }
// Check receipts. // Check receipts.
receipts, err := io.ReadAll(it.Receipts) rawReceipts, err := io.ReadAll(it.Receipts)
if err != nil {
t.Fatalf("error reading receipts from iterator: %v", err)
}
if !bytes.Equal(rawReceipts, chain.receipts[i]) {
t.Fatalf("mismatched receipts: want %s, got %s", chain.receipts[i], rawReceipts)
}
receipts, err := e.GetReceipts(i)
if err != nil { if err != nil {
t.Fatalf("error reading receipts: %v", err) t.Fatalf("error reading receipts: %v", err)
} }
if !bytes.Equal(receipts, chain.receipts[i]) { encReceipts, err := rlp.EncodeToBytes(receipts)
t.Fatalf("mismatched receipts: want %s, got %s", chain.receipts[i], receipts) if err != nil {
t.Fatalf("error encoding receipts: %v", err)
}
if !bytes.Equal(encReceipts, chain.receipts[i]) {
t.Fatalf("mismatched receipts: want %s, got %s", chain.receipts[i], encReceipts)
} }
// Check total difficulty. // Check total difficulty.
@ -153,155 +172,10 @@ func TestEraFilename(t *testing.T) {
} }
} }
func genTestChain(t *testing.T) (*core.Genesis, []*types.Block, []types.Receipts) { func mustEncode(obj any) []byte {
privateKey, err := crypto.GenerateKey() b, err := rlp.EncodeToBytes(obj)
require.NoError(t, err) if err != nil {
var ( panic(fmt.Sprintf("failed in encode obj: %v", err))
address = crypto.PubkeyToAddress(privateKey.PublicKey)
genesis = &core.Genesis{
Config: params.TestChainConfig,
Alloc: types.GenesisAlloc{
address: {
Balance: big.NewInt(10000000000000000), // 10 ETH
},
},
GasLimit: 1000000,
Difficulty: big.NewInt(1),
}
)
_, chain, receipts := core.GenerateChainWithGenesis(genesis, ethash.NewFaker(), 3, func(i int, gen *core.BlockGen) {
// Add a transfer transaction
to := common.HexToAddress("0x5678")
tx := types.NewTx(&types.LegacyTx{
Nonce: uint64(i * 2),
To: &to,
Value: big.NewInt(100000000000000), // 0.1 ETH
Gas: 21000,
GasPrice: big.NewInt(1000000000), // 1 Gwei
})
signedTx, err := types.SignTx(tx, types.NewEIP155Signer(genesis.Config.ChainID), privateKey)
require.NoError(t, err)
gen.AddTx(signedTx)
// Add a contract creation transaction
tx = types.NewTx(&types.LegacyTx{
Nonce: uint64(i*2 + 1),
Value: big.NewInt(0),
Gas: 100000,
GasPrice: big.NewInt(1000000000), // 1 Gwei
// Simple contract that returns empty data
Data: []byte{
0x60, 0x00, // PUSH1 0
0x60, 0x00, // PUSH1 0
0x52, // MSTORE
0x60, 0x20, // PUSH1 32
0x60, 0x00, // PUSH1 0
0xf3, // RETURN
},
})
signedTx, err = types.SignTx(tx, types.NewEIP155Signer(genesis.Config.ChainID), privateKey)
require.NoError(t, err)
gen.AddTx(signedTx)
})
return genesis, chain, receipts
}
// exportChain creates a temporary era file with the given chain.
func exportChain(t *testing.T, genesis *core.Genesis, chain []*types.Block, receipts []types.Receipts) (string, string) {
var (
tmpDir = t.TempDir()
fileName = "test.era1"
)
tmpFile, err := os.Create(filepath.Join(tmpDir, fileName))
require.NoError(t, err)
builder := NewBuilder(tmpFile)
// Add blocks to era
for i, block := range chain {
td := new(big.Int).Add(genesis.Difficulty, big.NewInt(int64(i+1)))
headerData, err := rlp.EncodeToBytes(block.Header())
require.NoError(t, err)
bodyData, err := rlp.EncodeToBytes(block.Body())
require.NoError(t, err)
receiptsData, err := rlp.EncodeToBytes(receipts[i])
require.NoError(t, err)
err = builder.AddRLP(headerData, bodyData, receiptsData, block.NumberU64(), block.Hash(), td, block.Difficulty())
require.NoError(t, err)
} }
return b
_, err = builder.Finalize()
require.NoError(t, err)
err = tmpFile.Close()
require.NoError(t, err)
return tmpDir, fileName
}
func TestEraFunctions(t *testing.T) {
genesis, blocks, receipts := genTestChain(t)
testdir, filename := exportChain(t, genesis, blocks, receipts)
defer os.RemoveAll(testdir)
// Open the era file
era, err := Open(filepath.Join(testdir, filename))
require.NoError(t, err)
defer era.Close()
t.Run("GetHeaderByNumber", func(t *testing.T) {
header, err := era.GetHeaderByNumber(era.Start())
require.NoError(t, err)
haveJson, err := rlp.EncodeToBytes(header)
require.NoError(t, err)
wantJson, err := rlp.EncodeToBytes(blocks[0].Header())
require.NoError(t, err)
require.Equal(t, wantJson, haveJson)
header, err = era.GetHeaderByNumber(era.Start() + era.Count() - 1)
require.NoError(t, err)
haveJson, err = rlp.EncodeToBytes(header)
require.NoError(t, err)
wantJson, err = rlp.EncodeToBytes(blocks[2].Header())
require.NoError(t, err)
require.Equal(t, wantJson, haveJson)
})
t.Run("GetBlockByNumber", func(t *testing.T) {
block, err := era.GetBlockByNumber(era.Start())
require.NoError(t, err)
haveJson, err := rlp.EncodeToBytes(block)
require.NoError(t, err)
wantJson, err := rlp.EncodeToBytes(blocks[0])
require.NoError(t, err)
require.Equal(t, wantJson, haveJson)
block, err = era.GetBlockByNumber(era.Start() + 1)
require.NoError(t, err)
haveJson, err = rlp.EncodeToBytes(block)
require.NoError(t, err)
wantJson, err = rlp.EncodeToBytes(blocks[1])
require.NoError(t, err)
require.Equal(t, wantJson, haveJson)
})
t.Run("GetReceipts", func(t *testing.T) {
rcpts, err := era.GetReceipts(era.Start())
require.NoError(t, err)
require.Equal(t, 2, len(rcpts)) // Should have 2 receipts
require.Equal(t, receipts[0][0].CumulativeGasUsed, rcpts[0].CumulativeGasUsed)
require.Equal(t, receipts[0][1].CumulativeGasUsed, rcpts[1].CumulativeGasUsed)
require.Equal(t, receipts[0][0].Status, rcpts[0].Status)
require.Equal(t, receipts[0][1].Status, rcpts[0].Status)
})
t.Run("OutOfBounds", func(t *testing.T) {
_, err := era.GetHeaderByNumber(era.Start() - 1)
require.Error(t, err)
require.Equal(t, "out-of-bounds", err.Error())
_, err = era.GetHeaderByNumber(era.Start() + era.Count())
require.Error(t, err)
require.Equal(t, "out-of-bounds", err.Error())
})
} }