@@ -51,6 +51,8 @@ pub const Enr = struct {
5151 attnets : ? [8 ]u8 ,
5252 /// Sync committee subnet bitfield (1 byte = 4 sync committees)
5353 syncnets : ? [1 ]u8 ,
54+ /// PeerDAS custody group count (`cgc` ENR field).
55+ custody_group_count : ? u64 ,
5456 /// Raw RLP of the entire record (for signature verification and re-encoding)
5557 raw : []u8 ,
5658 alloc : Allocator ,
@@ -110,6 +112,7 @@ pub fn decode(alloc: Allocator, data: []const u8) Error!Enr {
110112 .eth2_raw = null ,
111113 .attnets = null ,
112114 .syncnets = null ,
115+ .custody_group_count = null ,
113116 .raw = try alloc .dupe (u8 , data ),
114117 .alloc = alloc ,
115118 };
@@ -196,6 +199,13 @@ pub fn decode(alloc: Allocator, data: []const u8) Error!Enr {
196199 if (val .len == 8 ) {
197200 enr .attnets = val [0.. 8].* ;
198201 }
202+ } else if (std .mem .eql (u8 , key , "cgc" )) {
203+ const val = list .readBytes () catch return Error .InvalidEnr ;
204+ if (val .len > 0 and val .len <= 8 ) {
205+ var count : u64 = 0 ;
206+ for (val ) | b | count = (count << 8 ) | b ;
207+ enr .custody_group_count = count ;
208+ }
199209 } else if (std .mem .eql (u8 , key , "syncnets" )) {
200210 const val = list .readBytes () catch return Error .InvalidEnr ;
201211 if (val .len == 1 ) {
@@ -259,6 +269,8 @@ pub const Builder = struct {
259269 eth2 : ? [16 ]u8 = null ,
260270 /// Attestation subnet bitfield (8 bytes = 64 bits for 64 subnets)
261271 attnets : ? [8 ]u8 = null ,
272+ /// PeerDAS custody group count.
273+ custody_group_count : ? u64 = null ,
262274 /// Sync committee subnet bitfield (1 byte = 4 bits for 4 sync committees)
263275 syncnets : ? [1 ]u8 = null ,
264276
@@ -285,15 +297,28 @@ pub const Builder = struct {
285297 try writer .writeBytes (port_bytes [if (port_bytes [0 ] == 0 ) 1 else 0.. 2]);
286298 }
287299
300+ fn writeUint64Minimal (writer : * rlp.Writer , value : u64 ) ! void {
301+ var bytes : [8 ]u8 = undefined ;
302+ std .mem .writeInt (u64 , & bytes , value , .big );
303+
304+ var start : usize = 0 ;
305+ while (start < bytes .len - 1 and bytes [start ] == 0 ) : (start += 1 ) {}
306+ try writer .writeBytes (bytes [start .. ]);
307+ }
308+
288309 /// Write all key-value pairs in alphabetical order to an RLP writer.
289310 /// EIP-778 requires keys to be sorted.
290311 fn writeKVPairs (self : * const Builder , writer : * rlp.Writer , pubkey : * const [33 ]u8 ) ! void {
291- // Alphabetical order: attnets, eth2, id, ip, ip6, quic, quic6,
312+ // Alphabetical order: attnets, cgc, eth2, id, ip, ip6, quic, quic6,
292313 // secp256k1, syncnets, tcp, tcp6, udp, udp6
293314 if (self .attnets ) | attnets | {
294315 try writer .writeBytes ("attnets" );
295316 try writer .writeBytes (& attnets );
296317 }
318+ if (self .custody_group_count ) | count | {
319+ try writer .writeBytes ("cgc" );
320+ try writeUint64Minimal (writer , count );
321+ }
297322 if (self .eth2 ) | eth2_val | {
298323 try writer .writeBytes ("eth2" );
299324 try writer .writeBytes (& eth2_val );
@@ -474,6 +499,25 @@ test "ENR Builder: encode with eth2 and attnets" {
474499 try std .testing .expectEqual ([8 ]u8 { 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff }, parsed .attnets .? );
475500}
476501
502+ test "ENR Builder: encode with custody group count" {
503+ const hex_mod = @import ("hex.zig" );
504+ const alloc = std .testing .allocator ;
505+
506+ const secret_key = hex_mod .hexToBytesComptime (32 , "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291" );
507+ var builder = Builder .init (alloc , secret_key , 1 );
508+ builder .ip = [4 ]u8 { 127 , 0 , 0 , 1 };
509+ builder .udp = 9000 ;
510+ builder .custody_group_count = 12 ;
511+
512+ const enr_bytes = try builder .encode ();
513+ defer alloc .free (enr_bytes );
514+
515+ var parsed = try decode (alloc , enr_bytes );
516+ defer parsed .deinit ();
517+
518+ try std .testing .expectEqual (@as (? u64 , 12 ), parsed .custody_group_count );
519+ }
520+
477521test "ENR Builder: encodeToString produces valid enr: prefix" {
478522 const hex_mod = @import ("hex.zig" );
479523 const alloc = std .testing .allocator ;
0 commit comments