Skip to content
Draft
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
21 changes: 11 additions & 10 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ pub fn build(b: *std.Build) void {
const test_hex = b.addTest(.{
.name = "hex",
.root_module = module_hex,
.filters = b.option([][]const u8, "hex.filters", "hex test filters") orelse &[_][]const u8{},
.filters = &[_][]const u8{},
});
const install_test_hex = b.addInstallArtifact(test_hex, .{});
const tls_install_test_hex = b.step("build-test:hex", "Install the hex test");
Expand All @@ -133,7 +133,7 @@ pub fn build(b: *std.Build) void {
const test_constants = b.addTest(.{
.name = "constants",
.root_module = module_constants,
.filters = b.option([][]const u8, "constants.filters", "constants test filters") orelse &[_][]const u8{},
.filters = &[_][]const u8{},
});
const install_test_constants = b.addInstallArtifact(test_constants, .{});
const tls_install_test_constants = b.step("build-test:constants", "Install the constants test");
Expand All @@ -147,7 +147,7 @@ pub fn build(b: *std.Build) void {
const test_config = b.addTest(.{
.name = "config",
.root_module = module_config,
.filters = b.option([][]const u8, "config.filters", "config test filters") orelse &[_][]const u8{},
.filters = &[_][]const u8{},
});
const install_test_config = b.addInstallArtifact(test_config, .{});
const tls_install_test_config = b.step("build-test:config", "Install the config test");
Expand All @@ -161,7 +161,7 @@ pub fn build(b: *std.Build) void {
const test_consensus_types = b.addTest(.{
.name = "consensus_types",
.root_module = module_consensus_types,
.filters = b.option([][]const u8, "consensus_types.filters", "consensus_types test filters") orelse &[_][]const u8{},
.filters = &[_][]const u8{},
});
const install_test_consensus_types = b.addInstallArtifact(test_consensus_types, .{});
const tls_install_test_consensus_types = b.step("build-test:consensus_types", "Install the consensus_types test");
Expand All @@ -175,7 +175,7 @@ pub fn build(b: *std.Build) void {
const test_preset = b.addTest(.{
.name = "preset",
.root_module = module_preset,
.filters = b.option([][]const u8, "preset.filters", "preset test filters") orelse &[_][]const u8{},
.filters = &[_][]const u8{},
});
const install_test_preset = b.addInstallArtifact(test_preset, .{});
const tls_install_test_preset = b.step("build-test:preset", "Install the preset test");
Expand All @@ -189,7 +189,7 @@ pub fn build(b: *std.Build) void {
const test_state_transition = b.addTest(.{
.name = "state_transition",
.root_module = module_state_transition,
.filters = b.option([][]const u8, "state_transition.filters", "state_transition test filters") orelse &[_][]const u8{},
.filters = &[_][]const u8{},
});
const install_test_state_transition = b.addInstallArtifact(test_state_transition, .{});
const tls_install_test_state_transition = b.step("build-test:state_transition", "Install the state_transition test");
Expand All @@ -203,7 +203,7 @@ pub fn build(b: *std.Build) void {
const test_download_spec_tests = b.addTest(.{
.name = "download_spec_tests",
.root_module = module_download_spec_tests,
.filters = b.option([][]const u8, "download_spec_tests.filters", "download_spec_tests test filters") orelse &[_][]const u8{},
.filters = &[_][]const u8{},
});
const install_test_download_spec_tests = b.addInstallArtifact(test_download_spec_tests, .{});
const tls_install_test_download_spec_tests = b.step("build-test:download_spec_tests", "Install the download_spec_tests test");
Expand All @@ -217,7 +217,7 @@ pub fn build(b: *std.Build) void {
const test_write_spec_tests = b.addTest(.{
.name = "write_spec_tests",
.root_module = module_write_spec_tests,
.filters = b.option([][]const u8, "write_spec_tests.filters", "write_spec_tests test filters") orelse &[_][]const u8{},
.filters = &[_][]const u8{},
});
const install_test_write_spec_tests = b.addInstallArtifact(test_write_spec_tests, .{});
const tls_install_test_write_spec_tests = b.step("build-test:write_spec_tests", "Install the write_spec_tests test");
Expand All @@ -238,7 +238,7 @@ pub fn build(b: *std.Build) void {
const test_int = b.addTest(.{
.name = "int",
.root_module = module_int,
.filters = b.option([][]const u8, "int.filters", "int test filters") orelse &[_][]const u8{},
.filters = &[_][]const u8{},
});
const install_test_int = b.addInstallArtifact(test_int, .{});
const tls_install_test_int = b.step("build-test:int", "Install the int test");
Expand All @@ -259,7 +259,7 @@ pub fn build(b: *std.Build) void {
const test_spec_tests = b.addTest(.{
.name = "spec_tests",
.root_module = module_spec_tests,
.filters = b.option([][]const u8, "spec_tests.filters", "spec_tests test filters") orelse &[_][]const u8{},
.filters = &[_][]const u8{},
});
const install_test_spec_tests = b.addInstallArtifact(test_spec_tests, .{});
const tls_install_test_spec_tests = b.step("build-test:spec_tests", "Install the spec_tests test");
Expand Down Expand Up @@ -308,6 +308,7 @@ pub fn build(b: *std.Build) void {
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_spec_tests.addImport("spec_test_options", options_module_spec_test_options);
module_spec_tests.addImport("consensus_types", module_consensus_types);
Expand Down
2 changes: 1 addition & 1 deletion src/state_transition/block/process_block.zig
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ pub fn processBlock(
try processEth1Data(allocator, cached_state, body.eth1Data());
try processOperations(allocator, cached_state, body, opts);
if (state.isPostAltair()) {
try processSyncAggregate(allocator, cached_state, block, opts.verify_signature);
try processSyncAggregate(allocator, cached_state, body.syncAggregate(), opts.verify_signature);
}

if (state.isPostDeneb()) {
Expand Down
41 changes: 32 additions & 9 deletions src/state_transition/block/process_sync_committee.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const Block = @import("../types/signed_block.zig").Block;
const ValidatorIndex = ssz.primitive.ValidatorIndex.Type;
const AggregatedSignatureSet = @import("../utils/signature_sets.zig").AggregatedSignatureSet;
const ssz = @import("consensus_types");
const SyncAggregate = ssz.altair.SyncAggregate.Type;
const preset = @import("preset").preset;
const Root = ssz.primitive.Root.Type;
const G2_POINT_AT_INFINITY = @import("constants").G2_POINT_AT_INFINITY;
Expand All @@ -15,48 +16,68 @@ const BLSPubkey = ssz.primitive.BLSPubkey.Type;
const computeSigningRoot = @import("../utils/signing_root.zig").computeSigningRoot;
const verifyAggregatedSignatureSet = @import("../utils/signature_sets.zig").verifyAggregatedSignatureSet;
const balance_utils = @import("../utils/balance.zig");
const getBlockRootAtSlot = @import("../utils/block_root.zig").getBlockRootAtSlot;
const increaseBalance = balance_utils.increaseBalance;
const decreaseBalance = balance_utils.decreaseBalance;

pub fn processSyncAggregate(
allocator: Allocator,
cached_state: *CachedBeaconStateAllForks,
block: Block,
sync_aggregate: *const SyncAggregate,
verify_signatures: bool,
) !void {
const state = cached_state.state;
const epoch_cache = cached_state.getEpochCache();
const committee_indices = @as(*const [preset.SYNC_COMMITTEE_SIZE]u64, @ptrCast(epoch_cache.current_sync_committee_indexed.get().getValidatorIndices()));
const body = block.beaconBlockBody();
const committee_indices = @as(*const [preset.SYNC_COMMITTEE_SIZE]ValidatorIndex, @ptrCast(epoch_cache.current_sync_committee_indexed.get().getValidatorIndices()));
const sync_committee_bits = sync_aggregate.sync_committee_bits;
const signature = sync_aggregate.sync_committee_signature;

// different from the spec but not sure how to get through signature verification for default/empty SyncAggregate in the spec test
if (verify_signatures) {
const participant_indices = try body.syncAggregate().sync_committee_bits.intersectValues(
const participant_indices = try sync_committee_bits.intersectValues(
ValidatorIndex,
allocator,
committee_indices,
);
defer participant_indices.deinit();
const signature_set = try getSyncCommitteeSignatureSet(allocator, cached_state, block, participant_indices.items);

// When there's no participation we consider the signature valid and just ignore it
if (signature_set) |set| {
if (!try verifyAggregatedSignatureSet(&set)) {
if (participant_indices.items.len > 0) {
const previous_slot = @max(state.slot(), 1) - 1;
const root_signed = try getBlockRootAtSlot(state, previous_slot);
const domain = try cached_state.config.getDomain(state.slot(), c.DOMAIN_SYNC_COMMITTEE, previous_slot);

const pubkeys = try allocator.alloc(blst.PublicKey, participant_indices.items.len);
defer allocator.free(pubkeys);
for (0..participant_indices.items.len) |i| {
pubkeys[i] = epoch_cache.index_to_pubkey.items[participant_indices.items[i]];
}

var signing_root: Root = undefined;
try computeSigningRoot(ssz.primitive.Root, &root_signed, domain, &signing_root);

const signature_set = AggregatedSignatureSet{
.pubkeys = pubkeys,
.signing_root = signing_root,
.signature = signature,
};

if (!try verifyAggregatedSignatureSet(&signature_set)) {
return error.SyncCommitteeSignatureInvalid;
}
}
}

const sync_participant_reward = epoch_cache.sync_participant_reward;
const sync_proposer_reward = epoch_cache.sync_proposer_reward;
const sync_comittee_bits = body.syncAggregate().sync_committee_bits;
const proposer_index = try epoch_cache.getBeaconProposer(state.slot());
const balances = state.balances();
var proposer_balance = balances.items[proposer_index];

for (0..preset.SYNC_COMMITTEE_SIZE) |i| {
const index = committee_indices[i];

if (try sync_comittee_bits.get(i)) {
if (try sync_committee_bits.get(i)) {
// Positive rewards for participants
if (index == proposer_index) {
proposer_balance += sync_participant_reward;
Expand All @@ -82,6 +103,8 @@ pub fn processSyncAggregate(
}

/// Consumers should deinit the returned pubkeys
/// this is to be used when we implement getBlockSignatureSets
/// see https://github.com/ChainSafe/state-transition-z/issues/72
pub fn getSyncCommitteeSignatureSet(allocator: Allocator, cached_state: *const CachedBeaconStateAllForks, block: Block, participant_indices: ?[]usize) !?AggregatedSignatureSet {
const state = cached_state.state;
const epoch_cache = cached_state.getEpochCache();
Expand Down
9 changes: 9 additions & 0 deletions src/state_transition/test_utils/interop_pubkeys.zig
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const blst = @import("blst");
const bls_utils = @import("../utils/bls.zig");
const ssz = @import("consensus_types");
const BLSPubkey = ssz.primitive.BLSPubkey.Type;
const Secretkey = blst.SecretKey;
Expand All @@ -22,3 +23,11 @@ pub fn interopPubkeysCached(validator_count: usize, out: []BLSPubkey) !void {
out[i] = (pk.compress());
}
}

pub fn interopSign(validator_index: usize, message: []const u8) !blst.Signature {
var ikm = [_]u8{0} ** 32;
const u64_slice = std.mem.bytesAsSlice(u64, ikm[0..8]);
u64_slice[0] = @intCast(validator_index);
const sk = try Secretkey.keyGen(&ikm, null);
return bls_utils.sign(sk, message);
}
1 change: 1 addition & 0 deletions src/state_transition/test_utils/root.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const testing = std.testing;
/// Utils that could be used for different kinds of tests like int, perf
pub const TestCachedBeaconStateAllForks = @import("./generate_state.zig").TestCachedBeaconStateAllForks;
pub const generateElectraBlock = @import("./generate_block.zig").generateElectraBlock;
pub const interopSign = @import("./interop_pubkeys.zig").interopSign;

test {
testing.refAllDecls(@This());
Expand Down
3 changes: 2 additions & 1 deletion src/state_transition/types/signed_block.zig
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ pub const Body = union(enum) {
};
}

pub fn syncAggregate(self: *const Body) *const ssz.altair.SyncAggregate.Type {
pub fn syncAggregate(self: *const Body) *const SyncAggregate {
return switch (self.*) {
inline .regular, .blinded => |b| b.syncAggregate(),
};
Expand Down Expand Up @@ -149,6 +149,7 @@ const ConsolidationRequest = ssz.electra.ConsolidationRequest.Type;

const Attestation = @import("attestation.zig").Attestation;
const Attestations = @import("attestation.zig").Attestations;
const SyncAggregate = ssz.altair.SyncAggregate.Type;
const AttesterSlashings = @import("attester_slashing.zig").AttesterSlashings;
const ProposerSlashing = ssz.phase0.ProposerSlashing.Type;
const SignedVoluntaryExit = ssz.phase0.SignedVoluntaryExit.Type;
Expand Down
38 changes: 29 additions & 9 deletions test/int/process_sync_aggregate.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,50 @@ test "process sync aggregate - sanity" {
var test_state = try TestCachedBeaconStateAllForks.init(allocator, 256);
defer test_state.deinit();

const state = test_state.cached_state.state;
const config = test_state.cached_state.config;
const previous_slot = state.slot() - 1;
const root_signed = try state_transition.getBlockRootAtSlot(state, previous_slot);
const domain = try config.getDomain(state.slot(), c.DOMAIN_SYNC_COMMITTEE, previous_slot);
var signing_root: Root = undefined;
try computeSigningRoot(ssz.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 state_transition.test_utils.interopSign(committee_indices[0], &signing_root);
// validator 1 signs
const sig1 = try state_transition.test_utils.interopSign(committee_indices[1], &signing_root);
const agg_sig = try blst.AggregateSignature.aggregate(&.{ sig0, sig1 }, true);

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

var message: ssz.electra.BeaconBlock.Type = ssz.electra.BeaconBlock.default_value;
message.body = body;
const res = processSyncAggregate(allocator, test_state.cached_state, &sync_aggregate, true);
try std.testing.expect(res == error.SyncCommitteeSignatureInvalid);

const beacon_block = BeaconBlock{ .electra = &message };
const block = Block{ .regular = beacon_block };
try processSyncAggregate(allocator, test_state.cached_state, block, true);
// now set bit 1
try sync_aggregate.sync_committee_bits.set(1, true);
try processSyncAggregate(allocator, test_state.cached_state, &sync_aggregate, true);
}

const std = @import("std");
const ssz = @import("consensus_types");
const config = @import("config");
const blst = @import("blst");
const preset = @import("preset").preset;
const c = @import("constants");

const Allocator = std.mem.Allocator;
const TestCachedBeaconStateAllForks = @import("state_transition").test_utils.TestCachedBeaconStateAllForks;

const state_transition = @import("state_transition");
const processSyncAggregate = state_transition.processSyncAggregate;
const Root = ssz.primitive.Root.Type;
const ValidatorIndex = ssz.primitive.ValidatorIndex.Type;
const Block = state_transition.Block;
const SignedBlock = state_transition.SignedBlock;
const BeaconBlock = state_transition.BeaconBlock;
const SignedBeaconBlock = state_transition.SignedBeaconBlock;
const computeSigningRoot = state_transition.computeSigningRoot;
const G2_POINT_AT_INFINITY = @import("constants").G2_POINT_AT_INFINITY;
2 changes: 1 addition & 1 deletion test/spec/runner/Operations.zig
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ pub fn TestCase(comptime fork: ForkSeq, comptime operation: Operation, comptime
try state_transition.processProposerSlashing(self.pre.cached_state, &self.op, verify);
},
.sync_aggregate => {
return error.SkipZigTest;
try state_transition.processSyncAggregate(self.pre.cached_state, &self.op, verify);
},
.voluntary_exit => {
try state_transition.processVoluntaryExit(self.pre.cached_state, &self.op, verify);
Expand Down
1 change: 1 addition & 0 deletions zbuild.zon
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
.consensus_types,
.preset,
.constants,
.blst,
},
},
.filters = .{},
Expand Down