Skip to content

Commit d03f2dd

Browse files
feat: implement compact multi proofs and bindings for createMultiProof (#200)
This pr implements - multicompact proofs and uses [this](https://github.com/ChainSafe/ssz/blob/master/packages/persistent-merkle-tree/src/proof/multi.ts) as a reference - fixes a bug in `sszValueToNapiValue` to handle u64 types by using `createBigintUint64` instead of `createInt64` --------- Co-authored-by: Cayman <caymannava@gmail.com>
1 parent ac9bbd9 commit d03f2dd

5 files changed

Lines changed: 560 additions & 2 deletions

File tree

bindings/napi/BeaconStateView.zig

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -767,7 +767,79 @@ pub fn BeaconStateView_getSingleProof(env: napi.Env, cb: napi.CallbackInfo(1)) !
767767
return result;
768768
}
769769

770-
// pub fn BeaconStateView_createMultiProof
770+
/// Create a compact multi-proof from a descriptor.
771+
/// Arguments:
772+
/// - arg 0: descriptor (Uint8Array)
773+
/// Returns: {type: string, leaves: Uint8Array[], descriptor: Uint8Array}
774+
pub fn BeaconStateView_createMultiProof(env: napi.Env, cb: napi.CallbackInfo(1)) !napi.Value {
775+
const persistent_merkle_tree = @import("persistent_merkle_tree");
776+
const cached_state = try env.unwrap(CachedBeaconState, cb.this());
777+
778+
const descriptor_info = try cb.arg(0).getTypedarrayInfo();
779+
if (descriptor_info.array_type != .uint8) {
780+
try env.throwTypeError("Expected descriptor to be a Uint8Array");
781+
return env.getNull();
782+
}
783+
const descriptor = descriptor_info.data;
784+
785+
// Get the root node from the state
786+
try cached_state.state.commit();
787+
const root_node = switch (cached_state.state.*) {
788+
inline else => |*state| state.base_view.data.root,
789+
};
790+
791+
// Create proof input for compact multi-proof
792+
const proof_input = persistent_merkle_tree.proof.ProofInput{
793+
.compactMulti = .{ .descriptor = descriptor },
794+
};
795+
796+
var proof = persistent_merkle_tree.proof.createProof(
797+
allocator,
798+
&pool.pool,
799+
root_node,
800+
proof_input,
801+
) catch {
802+
try env.throwError("STATE_ERROR", "Failed to create proof");
803+
return env.getNull();
804+
};
805+
defer proof.deinit(allocator);
806+
807+
// Create result object matching Proof interface
808+
const result = try env.createObject();
809+
810+
// Add type field
811+
const proof_type_str = switch (proof) {
812+
inline else => |_, tag| @tagName(tag),
813+
};
814+
try result.setNamedProperty("type", try env.createStringUtf8(proof_type_str));
815+
816+
// Extract data based on proof type
817+
switch (proof) {
818+
.compactMulti => |compact| {
819+
// Create leaves array
820+
const leaves_array = try env.createArray();
821+
for (compact.leaves, 0..) |leaf, i| {
822+
var leaf_bytes: [*]u8 = undefined;
823+
const leaf_buf = try env.createArrayBuffer(32, &leaf_bytes);
824+
@memcpy(leaf_bytes[0..32], &leaf);
825+
try leaves_array.setElement(@intCast(i), try env.createTypedarray(.uint8, 32, leaf_buf, 0));
826+
}
827+
try result.setNamedProperty("leaves", leaves_array);
828+
829+
// Create descriptor Uint8Array
830+
var descriptor_bytes: [*]u8 = undefined;
831+
const descriptor_buf = try env.createArrayBuffer(compact.descriptor.len, &descriptor_bytes);
832+
@memcpy(descriptor_bytes[0..compact.descriptor.len], compact.descriptor);
833+
try result.setNamedProperty("descriptor", try env.createTypedarray(.uint8, compact.descriptor.len, descriptor_buf, 0));
834+
},
835+
else => {
836+
try env.throwError("STATE_ERROR", "Unexpected proof type");
837+
return env.getNull();
838+
},
839+
}
840+
841+
return result;
842+
}
771843

772844
pub fn BeaconStateView_computeUnrealizedCheckpoints(env: napi.Env, cb: napi.CallbackInfo(0)) !napi.Value {
773845
const cached_state = try env.unwrap(CachedBeaconState, cb.this());
@@ -995,7 +1067,7 @@ pub fn register(env: napi.Env, exports: napi.Value) !void {
9951067
method(0, BeaconStateView_getFinalizedRootProof),
9961068
// method(1, BeaconStateView_getSyncCommitteesWitness),
9971069
method(1, BeaconStateView_getSingleProof),
998-
// method(1, BeaconStateView_createMultiProof),
1070+
method(1, BeaconStateView_createMultiProof),
9991071

10001072
method(0, BeaconStateView_computeUnrealizedCheckpoints),
10011073

bindings/src/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ interface ProcessSlotsOpts {
5353
transferCache?: boolean;
5454
}
5555

56+
interface CompactMultiProof {
57+
type: "compactMulti";
58+
leaves: Uint8Array[];
59+
descriptor: Uint8Array;
60+
}
61+
5662
interface ProposerRewards {
5763
attestations: bigint;
5864
syncAggregate: bigint;
@@ -104,6 +110,7 @@ type VoluntaryExitValidity =
104110
| "pending_withdrawals"
105111
| "invalid_signature";
106112

113+
107114
declare class BeaconStateView {
108115
static createFromBytes(fork: string, bytes: Uint8Array): BeaconStateView;
109116

@@ -196,6 +203,7 @@ declare class BeaconStateView {
196203
serializedValidatorsSize(): number;
197204
serializeValidatorsToBytes(output: Uint8Array, offset: number): number;
198205
hashTreeRoot(): Uint8Array;
206+
createMultiProof(descriptor: Uint8Array): CompactMultiProof;
199207

200208
// stateTransition(signedBlockBytes: Uint8Array): BeaconStateView;
201209
processSlots(slot: number, options?: ProcessSlotsOpts): BeaconStateView;

bindings/test/demo.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ printDuration("proposerLookahead", () => state.proposerLookahead);
107107
printDuration("getSingleProof(169)", () => state.getSingleProof(169));
108108
printDuration("isValidVoluntaryExit", () => state.isValidVoluntaryExit(new Uint8Array(112), false));
109109
printDuration("getVoluntaryExitValidity", () => state.getVoluntaryExitValidity(new Uint8Array(112), false));
110+
printDuration("createMultiProof(descriptor for gindex 42)", () => state.createMultiProof(Uint8Array.from([0x25, 0xe0])));
110111
printDuration("getFinalizedRootProof()", () => state.getFinalizedRootProof());
111112
printDuration("isExecutionStateType", () => state.isExecutionStateType);
112113
printDuration("getEffectiveBalanceIncrementsZeroInactive()", () => state.getEffectiveBalanceIncrementsZeroInactive());

0 commit comments

Comments
 (0)