@@ -14,6 +14,7 @@ pub const std_options: std.Options = .{
1414};
1515
1616const types = @import ("@zeam/types" );
17+ const xmss = @import ("@zeam/xmss" );
1718const node_lib = @import ("@zeam/node" );
1819const Clock = node_lib .Clock ;
1920const state_proving_manager = @import ("@zeam/state-proving-manager" );
@@ -110,6 +111,21 @@ const BeamCmd = struct {
110111 }
111112};
112113
114+ /// Test-only CLI: sign a fixed message for (epoch, slot) and dump signature hex.
115+ const TestsigCmd = struct {
116+ help : bool = false ,
117+ @"private-key" : []const u8 ,
118+ epoch : u64 = 0 ,
119+ slot : u64 = 0 ,
120+
121+ pub const __messages__ = .{
122+ .@"private-key" = "Seed phrase for key generation (testing only)" ,
123+ .epoch = "Epoch number for signing" ,
124+ .slot = "Slot number (encoded in signed message)" ,
125+ .help = "Show help for testsig" ,
126+ };
127+ };
128+
113129const ZeamArgs = struct {
114130 genesis : u64 = 1234 ,
115131 log_filename : []const u8 = "consensus" , // Default logger filename
@@ -164,13 +180,15 @@ const ZeamArgs = struct {
164180 },
165181 },
166182 node : NodeCommand ,
183+ testsig : TestsigCmd ,
167184
168185 pub const __messages__ = .{
169186 .clock = "Run the clock service for slot timing" ,
170187 .beam = "Run a full Beam node" ,
171188 .prove = "Generate and verify ZK proofs for state transitions on a mock chain" ,
172189 .prometheus = "Prometheus configuration management" ,
173190 .node = "Run a lean node" ,
191+ .testsig = "Dump a signature for (private-key, epoch, slot); testing only" ,
174192 };
175193 },
176194
@@ -203,6 +221,7 @@ const ZeamArgs = struct {
203221 .genconfig = > | genconfig | try writer .print ("prometheus.genconfig(api_port={d}, filename=\" {s}\" )" , .{ genconfig .@"api-port" , genconfig .filename }),
204222 },
205223 .node = > | cmd | try writer .print ("node(node-id=\" {s}\" , custom_genesis=\" {s}\" , validator_config=\" {s}\" , data-dir=\" {s}\" , api_port={d})" , .{ cmd .@"node-id" , cmd .custom_genesis , cmd .validator_config , cmd .@"data-dir" , cmd .@"api-port" }),
224+ .testsig = > | cmd | try writer .print ("testsig(epoch={d}, slot={d})" , .{ cmd .epoch , cmd .slot }),
206225 }
207226 try writer .writeAll (")" );
208227 }
@@ -213,6 +232,9 @@ const ErrorHandler = error_handler.ErrorHandler;
213232
214233pub fn main () void {
215234 mainInner () catch | err | {
235+ if (err == error .MissingSubCommand ) {
236+ std .process .exit (1 );
237+ }
216238 ErrorHandler .handleApplicationError (err );
217239 std .process .exit (1 );
218240 };
@@ -232,10 +254,12 @@ fn mainInner() !void {
232254 const app_description = "Zeam - Zig implementation of Beam Chain, a ZK-based Ethereum Consensus Protocol" ;
233255 const app_version = build_options .version ;
234256
235- const opts = simargs .parse (allocator , ZeamArgs , app_description , app_version ) catch | err | {
257+ var parse_arena = std .heap .ArenaAllocator .init (allocator );
258+ defer parse_arena .deinit ();
259+
260+ const opts = simargs .parse (parse_arena .allocator (), ZeamArgs , app_description , app_version ) catch | err | {
236261 std .debug .print ("Failed to parse command-line arguments: {s}\n " , .{@errorName (err )});
237262 std .debug .print ("Run 'zeam --help' for usage information.\n " , .{});
238- ErrorHandler .logErrorWithOperation (err , "parse command-line arguments" );
239263 return err ;
240264 };
241265 defer opts .deinit ();
@@ -745,6 +769,43 @@ fn mainInner() !void {
745769 return err ;
746770 };
747771 },
772+ .testsig = > | cmd | {
773+ const num_active_epochs = @max (cmd .epoch + 1 , 1 );
774+ var keypair = xmss .KeyPair .generate (
775+ allocator ,
776+ cmd .@"private-key" ,
777+ 0 ,
778+ num_active_epochs ,
779+ ) catch | err | {
780+ ErrorHandler .logErrorWithOperation (err , "generate key from seed" );
781+ return err ;
782+ };
783+ defer keypair .deinit ();
784+
785+ var message : [32 ]u8 = [_ ]u8 {0 } ** 32 ;
786+ std .mem .writeInt (u64 , message [0.. 8], cmd .slot , .little );
787+
788+ const epoch_u32 : u32 = @intCast (cmd .epoch );
789+ var signature = keypair .sign (& message , epoch_u32 ) catch | err | {
790+ ErrorHandler .logErrorWithOperation (err , "sign message" );
791+ return err ;
792+ };
793+ defer signature .deinit ();
794+
795+ var sig_buf : [types .SIGSIZE ]u8 = undefined ;
796+ const bytes_written = signature .toBytes (& sig_buf ) catch | err | {
797+ ErrorHandler .logErrorWithOperation (err , "serialize signature" );
798+ return err ;
799+ };
800+
801+ const sig_slice = sig_buf [0.. bytes_written ];
802+ const hex_str = std .fmt .allocPrint (allocator , "0x{x}" , .{sig_slice }) catch | err | {
803+ ErrorHandler .logErrorWithOperation (err , "format signature hex" );
804+ return err ;
805+ };
806+ defer allocator .free (hex_str );
807+ std .debug .print ("signature: {s}\n " , .{hex_str });
808+ },
748809 }
749810}
750811
0 commit comments