Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,6 @@ jobs:
- name: Run state-transition tests
run: |
zig build test:state_transition
- name: Run Int Tests
run: |
zig build test:int

build-test-slow:
name: slow tests
Expand All @@ -126,7 +123,7 @@ jobs:
zig build run:download_era_files
- name: Run slow int tests
run: |
zig build test:int_slow
zig build test:int

spec-test-ssz:
name: spec tests - ssz
Expand Down
36 changes: 3 additions & 33 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -752,7 +752,7 @@ pub fn build(b: *std.Build) void {
tls_run_test.dependOn(&run_test_bench_process_epoch.step);

const module_int = b.createModule(.{
.root_source_file = b.path("test/int/root.zig"),
.root_source_file = b.path("test/int/era/root.zig"),
.target = target,
.optimize = optimize,
});
Expand All @@ -772,27 +772,6 @@ pub fn build(b: *std.Build) void {
tls_run_test_int.dependOn(&run_test_int.step);
tls_run_test.dependOn(&run_test_int.step);

const module_int_slow = b.createModule(.{
.root_source_file = b.path("test/int_slow/root.zig"),
.target = target,
.optimize = optimize,
});
b.modules.put(b.dupe("int_slow"), module_int_slow) catch @panic("OOM");

const test_int_slow = b.addTest(.{
.name = "int_slow",
.root_module = module_int_slow,
.filters = b.option([][]const u8, "int_slow.filters", "int_slow test filters") orelse &[_][]const u8{},
});
const install_test_int_slow = b.addInstallArtifact(test_int_slow, .{});
const tls_install_test_int_slow = b.step("build-test:int_slow", "Install the int_slow test");
tls_install_test_int_slow.dependOn(&install_test_int_slow.step);

const run_test_int_slow = b.addRunArtifact(test_int_slow);
const tls_run_test_int_slow = b.step("test:int_slow", "Run the int_slow test");
tls_run_test_int_slow.dependOn(&run_test_int_slow.step);
tls_run_test.dependOn(&run_test_int_slow.step);

const module_spec_tests = b.createModule(.{
.root_source_file = b.path("test/spec/root.zig"),
.target = target,
Expand Down Expand Up @@ -952,18 +931,9 @@ pub fn build(b: *std.Build) void {
module_bench_process_epoch.addImport("download_era_options", options_module_download_era_options);
module_bench_process_epoch.addImport("era", module_era);

module_int.addImport("build_options", options_module_build_options);
module_int.addImport("state_transition", module_state_transition);
module_int.addImport("config", module_config);
module_int.addImport("consensus_types", module_consensus_types);
module_int.addImport("preset", module_preset);
module_int.addImport("constants", module_constants);
module_int.addImport("blst", dep_blst.module("blst"));
module_int.addImport("persistent_merkle_tree", module_persistent_merkle_tree);

module_int_slow.addImport("config", module_config);
module_int_slow.addImport("download_era_options", options_module_download_era_options);
module_int_slow.addImport("era", module_era);
module_int.addImport("download_era_options", options_module_download_era_options);
module_int.addImport("era", module_era);

module_spec_tests.addImport("spec_test_options", options_module_spec_test_options);
module_spec_tests.addImport("consensus_types", module_consensus_types);
Expand Down
7 changes: 7 additions & 0 deletions src/state_transition/block/process_blob_kzg_commitments.zig
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,10 @@ pub fn processBlobKzgCommitments(external_data: BlockExternalData) !void {
else => {},
}
}

test "process blob kzg commitments - sanity" {
try processBlobKzgCommitments(.{
.execution_payload_status = .valid,
.data_availability_status = .available,
});
}
35 changes: 34 additions & 1 deletion src/state_transition/block/process_block_header.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ const Allocator = std.mem.Allocator;
const types = @import("consensus_types");
const CachedBeaconState = @import("../cache/state_cache.zig").CachedBeaconState;
const BeaconBlock = @import("../types/beacon_block.zig").BeaconBlock;
const BeaconConfig = @import("config").BeaconConfig;
const config = @import("config");
const BeaconConfig = config.BeaconConfig;
const BeaconBlockHeader = types.phase0.BeaconBlockHeader.Type;
const Root = types.primitive.Root;
const SignedBlock = @import("../types/block.zig").SignedBlock;
Expand Down Expand Up @@ -66,3 +67,35 @@ pub fn blockToHeader(allocator: Allocator, signed_block: SignedBlock, out: *Beac
out.state_root = block.stateRoot();
try block.hashTreeRoot(allocator, &out.body_root);
}

const TestCachedBeaconState = @import("../test_utils/root.zig").TestCachedBeaconState;
const preset = @import("preset").preset;
const Node = @import("persistent_merkle_tree").Node;

test "process block header - sanity" {
const allocator = std.testing.allocator;

var pool = try Node.Pool.init(allocator, 1024);
defer pool.deinit();

var test_state = try TestCachedBeaconState.init(allocator, &pool, 256);
const slot = config.mainnet.chain_config.ELECTRA_FORK_EPOCH * preset.SLOTS_PER_EPOCH + 2025 * preset.SLOTS_PER_EPOCH - 1;
defer test_state.deinit();

const proposers = test_state.cached_state.getEpochCache().proposers;

var message: types.electra.BeaconBlock.Type = types.electra.BeaconBlock.default_value;
const proposer_index = proposers[slot % preset.SLOTS_PER_EPOCH];

var header = try test_state.cached_state.state.latestBlockHeader();
const header_parent_root = try header.hashTreeRoot();

message.slot = slot;
message.proposer_index = proposer_index;
message.parent_root = header_parent_root.*;

const beacon_block = BeaconBlock{ .electra = &message };

const block = Block{ .regular = beacon_block };
try processBlockHeader(allocator, test_state.cached_state, block);
}
Comment on lines +75 to +101
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This sanity test is a good start, but it only verifies that processBlockHeader doesn't return an error. To make the test more robust, it should also assert that the function has the intended side effects on the state.

Specifically, processBlockHeader updates the latest_block_header in the state. You could add assertions to verify that the header has been updated correctly with the new block's data.

Additionally, the style guide (line 296) recommends adding a comment at the top of tests to explain their goal and methodology. Adding a brief description would improve clarity for future readers.

test "process block header - sanity" {
    // Verifies that processBlockHeader correctly updates the state's latest_block_header
    // with data from a valid block, without returning an error.
    const allocator = std.testing.allocator;

    var test_state = try TestCachedBeaconStateAllForks.init(allocator, 256);
    const slot = config.mainnet.chain_config.ELECTRA_FORK_EPOCH * preset.SLOTS_PER_EPOCH + 2025 * preset.SLOTS_PER_EPOCH - 1;
    defer test_state.deinit();

    const proposers = test_state.cached_state.getEpochCache().proposers;

    var message: types.electra.BeaconBlock.Type = types.electra.BeaconBlock.default_value;
    const proposer_index = proposers[slot % preset.SLOTS_PER_EPOCH];

    var header_parent_root: [32]u8 = undefined;
    try types.phase0.BeaconBlockHeader.hashTreeRoot(test_state.cached_state.state.latestBlockHeader(), &header_parent_root);

    message.slot = slot;
    message.proposer_index = proposer_index;
    message.parent_root = header_parent_root;

    const beacon_block = BeaconBlock{ .electra = &message };

    const block = Block{ .regular = beacon_block };
    try processBlockHeader(allocator, test_state.cached_state, block);

    // Assert that the latest block header has been updated.
    const new_header = test_state.cached_state.state.latestBlockHeader();
    try std.testing.expectEqual(slot, new_header.slot);
    try std.testing.expectEqual(proposer_index, new_header.proposer_index);
    try std.testing.expectEqualSlices(u8, &header_parent_root, &new_header.parent_root);
}
References
  1. The style guide recommends adding a description at the top of a test to explain its goal and methodology. This helps readers understand the test's purpose without having to read the implementation details. (link)
  2. The style guide emphasizes using assertions to detect programmer errors and ensure correctness. While this test checks for errors, it doesn't assert that the function's post-conditions are met, which is a key part of verifying its behavior. (link)

15 changes: 15 additions & 0 deletions src/state_transition/block/process_eth1_data.zig
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,18 @@ pub fn becomesNewEth1Data(allocator: std.mem.Allocator, state: *BeaconState, new

return false;
}

const TestCachedBeaconState = @import("../test_utils/root.zig").TestCachedBeaconState;
const Node = @import("persistent_merkle_tree").Node;

test "process eth1 data - sanity" {
const allocator = std.testing.allocator;
var pool = try Node.Pool.init(allocator, 1024);
defer pool.deinit();

var test_state = try TestCachedBeaconState.init(allocator, &pool, 256);
defer test_state.deinit();

const block = types.electra.BeaconBlock.default_value;
try processEth1Data(allocator, test_state.cached_state, &block.body.eth1_data);
}
Comment on lines +59 to +69
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similar to other new sanity tests, this one could be strengthened by asserting post-conditions. The processEth1Data function appends to eth1_data_votes. The test should verify that a vote has been added. This ensures the function is behaving as expected, not just that it runs without crashing.

test "process eth1 data - sanity" {
    const allocator = std.testing.allocator;

    var test_state = try TestCachedBeaconStateAllForks.init(allocator, 256);
    defer test_state.deinit();

    const block = types.electra.BeaconBlock.default_value;
    const votes_before = test_state.cached_state.state.eth1DataVotes().items.len;
    try processEth1Data(allocator, test_state.cached_state, &block.body.eth1_data);
    const votes_after = test_state.cached_state.state.eth1DataVotes().items.len;

    try std.testing.expect(votes_after == votes_before + 1);
}
References
  1. The style guide emphasizes using assertions to detect programmer errors and ensure correctness. While this test checks for errors, it doesn't assert that the function's post-conditions are met, which is a key part of verifying its behavior. (link)

45 changes: 40 additions & 5 deletions src/state_transition/block/process_execution_payload.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ const Allocator = std.mem.Allocator;
const CachedBeaconState = @import("../cache/state_cache.zig").CachedBeaconState;
const types = @import("consensus_types");
const preset = @import("preset").preset;
const ForkSeq = @import("config").ForkSeq;
const config = @import("config");
const ForkSeq = config.ForkSeq;
const SignedBlock = @import("../types/block.zig").SignedBlock;
const Body = @import("../types/block.zig").Body;
const ExecutionPayloadStatus = @import("../state_transition.zig").ExecutionPayloadStatus;
const ExecutionPayloadHeader = @import("../types/execution_payload.zig").ExecutionPayloadHeader;
const SignedBlindedBeaconBlock = @import("../types/beacon_block.zig").SignedBlindedBeaconBlock;
const BlockExternalData = @import("../state_transition.zig").BlockExternalData;
const BeaconConfig = @import("config").BeaconConfig;
const BeaconConfig = config.BeaconConfig;
const isMergeTransitionComplete = @import("../utils/execution.zig").isMergeTransitionComplete;
const computeEpochAtSlot = @import("../utils/epoch.zig").computeEpochAtSlot;
const getRandaoMix = @import("../utils/seed.zig").getRandaoMix;
Expand All @@ -29,8 +30,8 @@ pub fn processExecutionPayload(
external_data: BlockExternalData,
) !void {
const state = cached_state.state;
const beacon_config = cached_state.config;
const epoch_cache = cached_state.getEpochCache();
const config = epoch_cache.config;
var partial_payload = PartialPayload{};
switch (body) {
.regular => |b| {
Expand Down Expand Up @@ -72,12 +73,12 @@ pub fn processExecutionPayload(
// def compute_timestamp_at_slot(state: BeaconState, slot: Slot) -> uint64:
// slots_since_genesis = slot - GENESIS_SLOT
// return uint64(state.genesis_time + slots_since_genesis * SECONDS_PER_SLOT)
if (partial_payload.timestamp != (try state.genesisTime()) + (try state.slot()) * config.chain.SECONDS_PER_SLOT) {
if (partial_payload.timestamp != (try state.genesisTime() + try state.slot() * beacon_config.chain.SECONDS_PER_SLOT)) {
return error.InvalidExecutionPayloadTimestamp;
}

if (state.forkSeq().gte(.deneb)) {
const max_blobs_per_block = config.getMaxBlobsPerBlock(computeEpochAtSlot(try state.slot()));
const max_blobs_per_block = beacon_config.getMaxBlobsPerBlock(computeEpochAtSlot(try state.slot()));
if (body.blobKzgCommitmentsLen() > max_blobs_per_block) {
return error.BlobKzgCommitmentsExceedsLimit;
}
Expand Down Expand Up @@ -105,3 +106,37 @@ pub fn processExecutionPayload(

try state.setLatestExecutionPayloadHeader(&payload_header);
}

const BeaconBlock = @import("../types/beacon_block.zig").BeaconBlock;
const Block = @import("../types/block.zig").Block;
const TestCachedBeaconState = @import("../test_utils/root.zig").TestCachedBeaconState;
const Node = @import("persistent_merkle_tree").Node;

test "process execution payload - sanity" {
const allocator = std.testing.allocator;

var pool = try Node.Pool.init(allocator, 1024);
defer pool.deinit();

var test_state = try TestCachedBeaconState.init(allocator, &pool, 256);
defer test_state.deinit();

var execution_payload: types.electra.ExecutionPayload.Type = types.electra.ExecutionPayload.default_value;
const beacon_config = test_state.cached_state.config;
execution_payload.timestamp = try test_state.cached_state.state.genesisTime() + try test_state.cached_state.state.slot() * beacon_config.chain.SECONDS_PER_SLOT;
var body: types.electra.BeaconBlockBody.Type = types.electra.BeaconBlockBody.default_value;
body.execution_payload = execution_payload;

var message: types.electra.BeaconBlock.Type = types.electra.BeaconBlock.default_value;
message.body = body;

const beacon_block = BeaconBlock{ .electra = &message };
const block = Block{ .regular = beacon_block };

try processExecutionPayload(
allocator,
test_state.cached_state,
block.beaconBlockBody(),
.{ .execution_payload_status = .valid, .data_availability_status = .available },
);
}
Comment on lines +115 to +142
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This test correctly sets up a valid timestamp to pass the check within processExecutionPayload. To make it more comprehensive, you should add an assertion to verify that the state was updated correctly. processExecutionPayload calls state.setLatestExecutionPayloadHeader. A good assertion would be to check that the latest_execution_payload_header in the state has been updated and is no longer the default value.

test "process execution payload - sanity" {
    const allocator = std.testing.allocator;

    var test_state = try TestCachedBeaconStateAllForks.init(allocator, 256);
    defer test_state.deinit();

    var execution_payload: types.electra.ExecutionPayload.Type = types.electra.ExecutionPayload.default_value;
    execution_payload.timestamp = test_state.cached_state.state.genesisTime() + test_state.cached_state.state.slot() * config.mainnet.chain_config.SECONDS_PER_SLOT;
    // Use a non-default block hash to make the assertion meaningful.
    execution_payload.block_hash = [_]u8{1} ** 32;
    var body: types.electra.BeaconBlockBody.Type = types.electra.BeaconBlockBody.default_value;
    body.execution_payload = execution_payload;

    var message: types.electra.BeaconBlock.Type = types.electra.BeaconBlock.default_value;
    message.body = body;

    const beacon_block = BeaconBlock{ .electra = &message };
    const block = Block{ .regular = beacon_block };

    try processExecutionPayload(
        allocator,
        test_state.cached_state,
        block.beaconBlockBody(),
        .{ .execution_payload_status = .valid, .data_availability_status = .available },
    );

    const new_header = test_state.cached_state.state.latestExecutionPayloadHeader();
    try std.testing.expectEqualSlices(u8, &execution_payload.block_hash, &new_header.getBlockHash());
}
References
  1. The style guide emphasizes using assertions to detect programmer errors and ensure correctness. While this test checks for errors, it doesn't assert that the function's post-conditions are met, which is a key part of verifying its behavior. (link)

21 changes: 21 additions & 0 deletions src/state_transition/block/process_operations.zig
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,24 @@ pub fn processOperations(
}
}
}

const TestCachedBeaconState = @import("../test_utils/root.zig").TestCachedBeaconState;
const Block = @import("../types/block.zig").Block;
const BeaconBlock = @import("../types/beacon_block.zig").BeaconBlock;
const Node = @import("persistent_merkle_tree").Node;

test "process operations" {
const allocator = std.testing.allocator;

var pool = try Node.Pool.init(allocator, 1024);
defer pool.deinit();

var test_state = try TestCachedBeaconState.init(allocator, &pool, 256);
defer test_state.deinit();

const electra_block = types.electra.BeaconBlock.default_value;
const beacon_block = BeaconBlock{ .electra = &electra_block };

const block = Block{ .regular = beacon_block };
try processOperations(allocator, test_state.cached_state, block.beaconBlockBody(), .{});
}
34 changes: 34 additions & 0 deletions src/state_transition/block/process_randao.zig
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
const std = @import("std");
const CachedBeaconState = @import("../cache/state_cache.zig").CachedBeaconState;
const types = @import("consensus_types");
const preset = @import("preset").preset;
const config = @import("config");
const ForkSeq = config.ForkSeq;
const BeaconBlock = @import("../types/beacon_block.zig").BeaconBlock;
const Body = @import("../types/block.zig").Body;
const Bytes32 = types.primitive.Bytes32.Type;
const getRandaoMix = @import("../utils/seed.zig").getRandaoMix;
Expand Down Expand Up @@ -40,3 +44,33 @@ fn xor(a: *const [32]u8, b: *const [32]u8, out: *[32]u8) void {
out_i.* = a_i ^ b_i;
}
}

const TestCachedBeaconState = @import("../test_utils/root.zig").TestCachedBeaconState;
const Block = @import("../types/block.zig").Block;
const Node = @import("persistent_merkle_tree").Node;

test "process randao - sanity" {
const allocator = std.testing.allocator;

var pool = try Node.Pool.init(allocator, 1024);
defer pool.deinit();

var test_state = try TestCachedBeaconState.init(allocator, &pool, 256);
const slot = config.mainnet.chain_config.ELECTRA_FORK_EPOCH * preset.SLOTS_PER_EPOCH + 2025 * preset.SLOTS_PER_EPOCH - 1;
defer test_state.deinit();

const proposers = test_state.cached_state.getEpochCache().proposers;

var message: types.electra.BeaconBlock.Type = types.electra.BeaconBlock.default_value;
const proposer_index = proposers[slot % preset.SLOTS_PER_EPOCH];
var header = try test_state.cached_state.state.latestBlockHeader();
const header_parent_root = try header.hashTreeRoot();

message.slot = slot;
message.proposer_index = proposer_index;
message.parent_root = header_parent_root.*;

const beacon_block = BeaconBlock{ .electra = &message };
const block = Block{ .regular = beacon_block };
try processRandao(test_state.cached_state, block.beaconBlockBody(), block.proposerIndex(), false);
}
Comment on lines +52 to +76
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This sanity test is good for ensuring the function runs without errors on a valid state. To improve it, you could add an assertion to check that the randao_mixes in the state is actually updated by processRandao. You can store the old mix, call the function, and then assert that the new mix is different.

test "process randao - sanity" {
    const allocator = std.testing.allocator;

    var test_state = try TestCachedBeaconStateAllForks.init(allocator, 256);
    const slot = config.mainnet.chain_config.ELECTRA_FORK_EPOCH * preset.SLOTS_PER_EPOCH + 2025 * preset.SLOTS_PER_EPOCH - 1;
    defer test_state.deinit();

    const epoch = test_state.cached_state.getEpochCache().epoch;
    const old_mix = test_state.cached_state.state.randaoMixes()[epoch % preset.EPOCHS_PER_HISTORICAL_VECTOR];

    const proposers = test_state.cached_state.getEpochCache().proposers;

    var message: types.electra.BeaconBlock.Type = types.electra.BeaconBlock.default_value;
    const proposer_index = proposers[slot % preset.SLOTS_PER_EPOCH];

    var header_parent_root: [32]u8 = undefined;
    try types.phase0.BeaconBlockHeader.hashTreeRoot(test_state.cached_state.state.latestBlockHeader(), &header_parent_root);

    message.slot = slot;
    message.proposer_index = proposer_index;
    message.parent_root = header_parent_root;

    const beacon_block = BeaconBlock{ .electra = &message };
    const block = Block{ .regular = beacon_block };
    try processRandao(test_state.cached_state, block.beaconBlockBody(), block.proposerIndex(), false);

    const new_mix = test_state.cached_state.state.randaoMixes()[epoch % preset.EPOCHS_PER_HISTORICAL_VECTOR];
    try std.testing.expect(!std.mem.eql(u8, &old_mix, &new_mix));
}
References
  1. The style guide emphasizes using assertions to detect programmer errors and ensure correctness. While this test checks for errors, it doesn't assert that the function's post-conditions are met, which is a key part of verifying its behavior. (link)

41 changes: 41 additions & 0 deletions src/state_transition/block/process_sync_committee.zig
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,44 @@ pub fn getSyncCommitteeSignatureSet(allocator: Allocator, cached_state: *CachedB
.signature = signature,
};
}

const TestCachedBeaconState = @import("../test_utils/root.zig").TestCachedBeaconState;
const test_utils = @import("../test_utils/root.zig");
const Node = @import("persistent_merkle_tree").Node;

test "process sync aggregate - sanity" {
const allocator = std.testing.allocator;

var pool = try Node.Pool.init(allocator, 1024);
defer pool.deinit();

var test_state = try TestCachedBeaconState.init(allocator, &pool, 256);
defer test_state.deinit();

const state = test_state.cached_state.state;
const config = test_state.cached_state.config;
const previous_slot = try state.slot() - 1;
const root_signed = try getBlockRootAtSlot(state, previous_slot);
const domain = try config.getDomain(try state.slot(), c.DOMAIN_SYNC_COMMITTEE, previous_slot);
var signing_root: Root = undefined;
try computeSigningRoot(types.primitive.Root, root_signed, domain, &signing_root);

const committee_indices = @as(*const [preset.SYNC_COMMITTEE_SIZE]ValidatorIndex, @ptrCast(test_state.cached_state.getEpochCache().current_sync_committee_indexed.get().getValidatorIndices()));
// validator 0 signs
const sig0 = try test_utils.interopSign(committee_indices[0], &signing_root);
// validator 1 signs
const sig1 = try test_utils.interopSign(committee_indices[1], &signing_root);
const agg_sig = try blst.AggregateSignature.aggregate(&.{ sig0, sig1 }, true);

var sync_aggregate: types.electra.SyncAggregate.Type = types.electra.SyncAggregate.default_value;
sync_aggregate.sync_committee_signature = agg_sig.toSignature().compress();
try sync_aggregate.sync_committee_bits.set(0, true);
// don't set bit 1 yet

const res = processSyncAggregate(allocator, test_state.cached_state, &sync_aggregate, true);
try std.testing.expect(res == error.SyncCommitteeSignatureInvalid);

// now set bit 1
try sync_aggregate.sync_committee_bits.set(1, true);
try processSyncAggregate(allocator, test_state.cached_state, &sync_aggregate, true);
}
28 changes: 28 additions & 0 deletions src/state_transition/block/process_withdrawals.zig
Original file line number Diff line number Diff line change
Expand Up @@ -218,3 +218,31 @@ pub fn getExpectedWithdrawals(
withdrawals_result.sampled_validators = n;
withdrawals_result.processed_partial_withdrawals_count = processed_partial_withdrawals_count;
}
const TestCachedBeaconState = @import("../test_utils/root.zig").TestCachedBeaconState;
const Node = @import("persistent_merkle_tree").Node;

test "process withdrawals - sanity" {
const allocator = std.testing.allocator;

var pool = try Node.Pool.init(allocator, 1024);
defer pool.deinit();

var test_state = try TestCachedBeaconState.init(allocator, &pool, 256);
defer test_state.deinit();

var withdrawals_result = WithdrawalsResult{
.withdrawals = try Withdrawals.initCapacity(
allocator,
preset.MAX_WITHDRAWALS_PER_PAYLOAD,
),
};
defer withdrawals_result.withdrawals.deinit(allocator);
var withdrawal_balances = std.AutoHashMap(ValidatorIndex, usize).init(allocator);
defer withdrawal_balances.deinit();

var root: Root = undefined;
try types.capella.Withdrawals.hashTreeRoot(allocator, &withdrawals_result.withdrawals, &root);

try getExpectedWithdrawals(allocator, &withdrawals_result, &withdrawal_balances, test_state.cached_state);
try processWithdrawals(allocator, test_state.cached_state, withdrawals_result, root);
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,15 @@ pub fn processEffectiveBalanceUpdates(allocator: Allocator, cached_state: *Cache
cache.next_epoch_total_active_balance_by_increment = next_epoch_total_active_balance_by_increment;
return num_update;
}

test "processEffectiveBalanceUpdates - sanity" {
try @import("../test_utils/test_runner.zig").TestRunner(
processEffectiveBalanceUpdates,
.{
.alloc = true,
.err_return = true,
.void_return = false,
},
).testProcessEpochFn();
defer @import("../state_transition.zig").deinitStateTransition();
}
Comment on lines +114 to +124
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using a TestRunner for a basic sanity check is convenient, but for a function like processEffectiveBalanceUpdates, a more specific test case would be more valuable. The current test runs on a default state where no effective balances are likely to be updated.

Consider adding a dedicated test that sets up a validator with a balance that would trigger the hysteresis update, then call processEffectiveBalanceUpdates and assert that the validator's effective_balance has been updated as expected. This would provide a much stronger guarantee of correctness.

References
  1. The style guide emphasizes using assertions to detect programmer errors and ensure correctness. While this test checks for errors, it doesn't assert that the function's post-conditions are met, which is a key part of verifying its behavior. (link)

Loading