From fa8d0b31af01f40c47926ad7233d11ae9b518e7c Mon Sep 17 00:00:00 2001 From: Cayman Date: Sat, 22 Nov 2025 13:24:34 -0500 Subject: [PATCH 1/4] feat: reuse bytes when deserializing gossip --- packages/api/package.json | 2 +- packages/beacon-node/package.json | 2 +- packages/beacon-node/src/network/gossip/topic.ts | 2 +- packages/cli/package.json | 2 +- packages/config/package.json | 2 +- packages/db/package.json | 2 +- packages/fork-choice/package.json | 2 +- packages/light-client/package.json | 2 +- packages/state-transition/package.json | 2 +- packages/types/package.json | 2 +- packages/validator/package.json | 2 +- yarn.lock | 8 ++++---- 12 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/api/package.json b/packages/api/package.json index c99fd0f48704..8afa1f9045d1 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -75,7 +75,7 @@ }, "dependencies": { "@chainsafe/persistent-merkle-tree": "^1.2.1", - "@chainsafe/ssz": "^1.2.2", + "@chainsafe/ssz": "^1.3.0", "@lodestar/config": "^1.36.0", "@lodestar/params": "^1.36.0", "@lodestar/types": "^1.36.0", diff --git a/packages/beacon-node/package.json b/packages/beacon-node/package.json index 6c90077d6b3d..3d18b435acbe 100644 --- a/packages/beacon-node/package.json +++ b/packages/beacon-node/package.json @@ -123,7 +123,7 @@ "@chainsafe/persistent-merkle-tree": "^1.2.1", "@chainsafe/prometheus-gc-stats": "^1.0.0", "@chainsafe/pubkey-index-map": "^3.0.0", - "@chainsafe/ssz": "^1.2.2", + "@chainsafe/ssz": "^1.3.0", "@chainsafe/threads": "^1.11.3", "@crate-crypto/node-eth-kzg": "0.9.1", "@ethersproject/abi": "^5.7.0", diff --git a/packages/beacon-node/src/network/gossip/topic.ts b/packages/beacon-node/src/network/gossip/topic.ts index e460c02c9157..181da56d7f7f 100644 --- a/packages/beacon-node/src/network/gossip/topic.ts +++ b/packages/beacon-node/src/network/gossip/topic.ts @@ -123,7 +123,7 @@ export function getGossipSSZType(topic: GossipTopic) { export function sszDeserialize(topic: T, serializedData: Uint8Array): SSZTypeOfGossipTopic { const sszType = getGossipSSZType(topic); try { - return sszType.deserialize(serializedData) as SSZTypeOfGossipTopic; + return sszType.deserialize(serializedData, {reuseBytes: true}) as SSZTypeOfGossipTopic; } catch (_e) { throw new GossipActionError(GossipAction.REJECT, {code: GossipErrorCode.INVALID_SERIALIZED_BYTES_ERROR_CODE}); } diff --git a/packages/cli/package.json b/packages/cli/package.json index be07274c9d13..cabb355206f6 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -64,7 +64,7 @@ "@chainsafe/discv5": "^11.0.4", "@chainsafe/enr": "^5.0.1", "@chainsafe/persistent-merkle-tree": "^1.2.1", - "@chainsafe/ssz": "^1.2.2", + "@chainsafe/ssz": "^1.3.0", "@chainsafe/threads": "^1.11.3", "@libp2p/crypto": "^5.0.15", "@libp2p/interface": "^2.7.0", diff --git a/packages/config/package.json b/packages/config/package.json index f5318c598a6e..dcb8c4da9a1d 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -60,7 +60,7 @@ "blockchain" ], "dependencies": { - "@chainsafe/ssz": "^1.2.2", + "@chainsafe/ssz": "^1.3.0", "@lodestar/params": "^1.36.0", "@lodestar/types": "^1.36.0", "@lodestar/utils": "^1.36.0" diff --git a/packages/db/package.json b/packages/db/package.json index 7a391f0f4e6a..b02afdad5820 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -43,7 +43,7 @@ "check-readme": "typescript-docs-verifier" }, "dependencies": { - "@chainsafe/ssz": "^1.2.2", + "@chainsafe/ssz": "^1.3.0", "@lodestar/config": "^1.36.0", "@lodestar/utils": "^1.36.0", "classic-level": "^1.4.1", diff --git a/packages/fork-choice/package.json b/packages/fork-choice/package.json index b8268d453516..ea0c1aee0822 100644 --- a/packages/fork-choice/package.json +++ b/packages/fork-choice/package.json @@ -39,7 +39,7 @@ "check-readme": "typescript-docs-verifier" }, "dependencies": { - "@chainsafe/ssz": "^1.2.2", + "@chainsafe/ssz": "^1.3.0", "@lodestar/config": "^1.36.0", "@lodestar/params": "^1.36.0", "@lodestar/state-transition": "^1.36.0", diff --git a/packages/light-client/package.json b/packages/light-client/package.json index 31fed2dacf78..a8354e8e52c7 100644 --- a/packages/light-client/package.json +++ b/packages/light-client/package.json @@ -66,7 +66,7 @@ "@chainsafe/bls": "8.2.0", "@chainsafe/blst": "^2.2.0", "@chainsafe/persistent-merkle-tree": "^1.2.1", - "@chainsafe/ssz": "^1.2.2", + "@chainsafe/ssz": "^1.3.0", "@lodestar/api": "^1.36.0", "@lodestar/config": "^1.36.0", "@lodestar/params": "^1.36.0", diff --git a/packages/state-transition/package.json b/packages/state-transition/package.json index fb814d081b6a..1290972f0099 100644 --- a/packages/state-transition/package.json +++ b/packages/state-transition/package.json @@ -60,7 +60,7 @@ "@chainsafe/persistent-merkle-tree": "^1.2.1", "@chainsafe/persistent-ts": "^1.0.0", "@chainsafe/pubkey-index-map": "^3.0.0", - "@chainsafe/ssz": "^1.2.2", + "@chainsafe/ssz": "^1.3.0", "@chainsafe/swap-or-not-shuffle": "^1.2.1", "@lodestar/config": "^1.36.0", "@lodestar/params": "^1.36.0", diff --git a/packages/types/package.json b/packages/types/package.json index caf2c72c8dbc..595f23cd73a0 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -76,7 +76,7 @@ }, "types": "lib/index.d.ts", "dependencies": { - "@chainsafe/ssz": "^1.2.2", + "@chainsafe/ssz": "^1.3.0", "@lodestar/params": "^1.36.0", "ethereum-cryptography": "^2.0.0" }, diff --git a/packages/validator/package.json b/packages/validator/package.json index 4883b8ed4054..c1fef8ae6024 100644 --- a/packages/validator/package.json +++ b/packages/validator/package.json @@ -49,7 +49,7 @@ ], "dependencies": { "@chainsafe/blst": "^2.2.0", - "@chainsafe/ssz": "^1.2.2", + "@chainsafe/ssz": "^1.3.0", "@lodestar/api": "^1.36.0", "@lodestar/config": "^1.36.0", "@lodestar/db": "^1.36.0", diff --git a/yarn.lock b/yarn.lock index 78e9ae06c4d4..b6bc4b70ed52 100644 --- a/yarn.lock +++ b/yarn.lock @@ -718,10 +718,10 @@ "@chainsafe/as-sha256" "^0.4.1" "@chainsafe/persistent-merkle-tree" "^0.6.1" -"@chainsafe/ssz@^1.2.2": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@chainsafe/ssz/-/ssz-1.2.2.tgz#3c41b70bac9e646cb4cce0d49bda176ca87984ad" - integrity sha512-kIA3fJO6h2RsQndsNBlCSQYB4xfdZGMQvNPKPgbiB0mysV6okuxeJU3Nyl16xDCKv3tqej76eGYHcyjMVt7V1w== +"@chainsafe/ssz@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@chainsafe/ssz/-/ssz-1.3.0.tgz#dd9feebc096f9c726d7f7795e79032b41cbaf3c0" + integrity sha512-+O3WHnud8ZzIVF9/hSC+UazLDPgaa98quKLj/Z1PGnfGuW4JvCfS5Wh9EoZDvqGTI/AZlIbXpn4qC2s4BzQdfA== dependencies: "@chainsafe/as-sha256" "1.2.0" "@chainsafe/persistent-merkle-tree" "1.2.1" From 71d0133dda60d1cdc55191c9c65703def39239ef Mon Sep 17 00:00:00 2001 From: Cayman Date: Sat, 22 Nov 2025 13:43:42 -0500 Subject: [PATCH 2/4] feat: use reuseBytes for deserialized attestations --- packages/beacon-node/src/network/gossip/topic.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/beacon-node/src/network/gossip/topic.ts b/packages/beacon-node/src/network/gossip/topic.ts index 181da56d7f7f..78a8656b0700 100644 --- a/packages/beacon-node/src/network/gossip/topic.ts +++ b/packages/beacon-node/src/network/gossip/topic.ts @@ -148,9 +148,9 @@ export function sszDeserializeAttestation(fork: ForkName, serializedData: Uint8A export function sszDeserializeSingleAttestation(fork: ForkName, serializedData: Uint8Array): SingleAttestation { try { if (isForkPostElectra(fork)) { - return sszTypesFor(fork).SingleAttestation.deserialize(serializedData); + return sszTypesFor(fork).SingleAttestation.deserialize(serializedData, {reuseBytes: true}); } - return sszTypesFor(fork).Attestation.deserialize(serializedData) as SingleAttestation; + return sszTypesFor(fork).Attestation.deserialize(serializedData, {reuseBytes: true}) as SingleAttestation; } catch (_e) { throw new GossipActionError(GossipAction.REJECT, {code: GossipErrorCode.INVALID_SERIALIZED_BYTES_ERROR_CODE}); } From 6b17dcc1f2aa23c0b85ec1645fb705a5d7a4586a Mon Sep 17 00:00:00 2001 From: Tuyen Nguyen Date: Tue, 25 Nov 2025 10:28:20 +0700 Subject: [PATCH 3/4] fix: broken api ResponseDataCodec type --- packages/api/src/beacon/routes/validator.ts | 8 ++++---- packages/api/src/utils/client/response.ts | 2 +- packages/api/src/utils/codecs.ts | 6 ++++-- packages/api/src/utils/server/handler.ts | 5 ++++- packages/api/src/utils/types.ts | 2 +- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/packages/api/src/beacon/routes/validator.ts b/packages/api/src/beacon/routes/validator.ts index e56675ddfd28..64bef7fa8742 100644 --- a/packages/api/src/beacon/routes/validator.ts +++ b/packages/api/src/beacon/routes/validator.ts @@ -707,12 +707,12 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions // <- tranformation ); }, - deserialize(data, {executionPayloadBlinded, version}) { + deserialize(data, opts, {executionPayloadBlinded, version}) { return executionPayloadBlinded - ? getPostBellatrixForkTypes(version).BlindedBeaconBlock.deserialize(data) + ? getPostBellatrixForkTypes(version).BlindedBeaconBlock.deserialize(data, opts) : isForkPostDeneb(version) - ? sszTypesFor(version).BlockContents.deserialize(data) - : {block: ssz[version].BeaconBlock.deserialize(data)}; // <- tranformation + ? sszTypesFor(version).BlockContents.deserialize(data, opts) + : {block: ssz[version].BeaconBlock.deserialize(data, opts)}; // <- tranformation }, }, meta: { diff --git a/packages/api/src/utils/client/response.ts b/packages/api/src/utils/client/response.ts index 626252b9aaca..84186f35b9f7 100644 --- a/packages/api/src/utils/client/response.ts +++ b/packages/api/src/utils/client/response.ts @@ -116,7 +116,7 @@ export class ApiResponse extends Response { break; } case WireFormat.ssz: - this._value = this.definition.resp.data.deserialize(rawBody.value, meta); + this._value = this.definition.resp.data.deserialize(rawBody.value, undefined, meta); break; } } diff --git a/packages/api/src/utils/codecs.ts b/packages/api/src/utils/codecs.ts index fad6396af085..e2b16a2453f2 100644 --- a/packages/api/src/utils/codecs.ts +++ b/packages/api/src/utils/codecs.ts @@ -83,7 +83,8 @@ export function WithMeta(getType: (m: M) => Ty toJson: (data, meta: M) => getType(meta).toJson(data), fromJson: (data, meta: M) => getType(meta).fromJson(data), serialize: (data, meta: M) => getType(meta).serialize(data), - deserialize: (data, meta: M) => getType(meta).deserialize(data), + deserialize: (data, opts: {reuseBytes?: boolean | undefined} | undefined, meta: M) => + getType(meta).deserialize(data, opts), }; } @@ -94,7 +95,8 @@ export function WithVersion( toJson: (data, meta: M) => getType(meta.version).toJson(data), fromJson: (data, meta: M) => getType(meta.version).fromJson(data), serialize: (data, meta: M) => getType(meta.version).serialize(data), - deserialize: (data, meta: M) => getType(meta.version).deserialize(data), + deserialize: (data, opts: {reuseBytes?: boolean | undefined} | undefined, meta: M) => + getType(meta.version).deserialize(data, opts), }; } diff --git a/packages/api/src/utils/server/handler.ts b/packages/api/src/utils/server/handler.ts index b444d263cf09..b5143be29c3b 100644 --- a/packages/api/src/utils/server/handler.ts +++ b/packages/api/src/utils/server/handler.ts @@ -118,7 +118,10 @@ export function createFastifyHandler( void resp.headers(metaHeaders); const data = response?.data instanceof Uint8Array - ? definition.resp.data.toJson(definition.resp.data.deserialize(response.data, response.meta), response.meta) + ? definition.resp.data.toJson( + definition.resp.data.deserialize(response.data, undefined, response.meta), + response.meta + ) : definition.resp.data.toJson(response?.data, response?.meta); const metaJson = definition.resp.meta.toJson(response?.meta); if (definition.resp.transform) { diff --git a/packages/api/src/utils/types.ts b/packages/api/src/utils/types.ts index aef18b47d940..15017deccba9 100644 --- a/packages/api/src/utils/types.ts +++ b/packages/api/src/utils/types.ts @@ -118,7 +118,7 @@ export type ResponseDataCodec = { toJson: (data: T, meta: M) => unknown; // server fromJson: (data: unknown, meta: M) => T; // client serialize: (data: T, meta: M) => Uint8Array; // server - deserialize: (data: Uint8Array, meta: M) => T; // client + deserialize: (data: Uint8Array, opts: {reuseBytes?: boolean | undefined} | undefined, meta: M) => T; // client }; export type ResponseMetadataCodec = { From ab5b9198746167d85b3c331b5e3d2891c74828f5 Mon Sep 17 00:00:00 2001 From: Nico Flaig Date: Thu, 27 Nov 2025 12:10:29 +0100 Subject: [PATCH 4/4] Simplify type --- packages/api/src/utils/client/response.ts | 2 +- packages/api/src/utils/codecs.ts | 5 ++--- packages/api/src/utils/server/handler.ts | 2 +- packages/api/src/utils/types.ts | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/api/src/utils/client/response.ts b/packages/api/src/utils/client/response.ts index 84186f35b9f7..40afbc1c1ac2 100644 --- a/packages/api/src/utils/client/response.ts +++ b/packages/api/src/utils/client/response.ts @@ -116,7 +116,7 @@ export class ApiResponse extends Response { break; } case WireFormat.ssz: - this._value = this.definition.resp.data.deserialize(rawBody.value, undefined, meta); + this._value = this.definition.resp.data.deserialize(rawBody.value, {}, meta); break; } } diff --git a/packages/api/src/utils/codecs.ts b/packages/api/src/utils/codecs.ts index e2b16a2453f2..b51f1d0bac65 100644 --- a/packages/api/src/utils/codecs.ts +++ b/packages/api/src/utils/codecs.ts @@ -83,8 +83,7 @@ export function WithMeta(getType: (m: M) => Ty toJson: (data, meta: M) => getType(meta).toJson(data), fromJson: (data, meta: M) => getType(meta).fromJson(data), serialize: (data, meta: M) => getType(meta).serialize(data), - deserialize: (data, opts: {reuseBytes?: boolean | undefined} | undefined, meta: M) => - getType(meta).deserialize(data, opts), + deserialize: (data, opts: {reuseBytes?: boolean} | undefined, meta: M) => getType(meta).deserialize(data, opts), }; } @@ -95,7 +94,7 @@ export function WithVersion( toJson: (data, meta: M) => getType(meta.version).toJson(data), fromJson: (data, meta: M) => getType(meta.version).fromJson(data), serialize: (data, meta: M) => getType(meta.version).serialize(data), - deserialize: (data, opts: {reuseBytes?: boolean | undefined} | undefined, meta: M) => + deserialize: (data, opts: {reuseBytes?: boolean} | undefined, meta: M) => getType(meta.version).deserialize(data, opts), }; } diff --git a/packages/api/src/utils/server/handler.ts b/packages/api/src/utils/server/handler.ts index b5143be29c3b..97dbb93f269e 100644 --- a/packages/api/src/utils/server/handler.ts +++ b/packages/api/src/utils/server/handler.ts @@ -119,7 +119,7 @@ export function createFastifyHandler( const data = response?.data instanceof Uint8Array ? definition.resp.data.toJson( - definition.resp.data.deserialize(response.data, undefined, response.meta), + definition.resp.data.deserialize(response.data, {}, response.meta), response.meta ) : definition.resp.data.toJson(response?.data, response?.meta); diff --git a/packages/api/src/utils/types.ts b/packages/api/src/utils/types.ts index 15017deccba9..f940bbde5124 100644 --- a/packages/api/src/utils/types.ts +++ b/packages/api/src/utils/types.ts @@ -118,7 +118,7 @@ export type ResponseDataCodec = { toJson: (data: T, meta: M) => unknown; // server fromJson: (data: unknown, meta: M) => T; // client serialize: (data: T, meta: M) => Uint8Array; // server - deserialize: (data: Uint8Array, opts: {reuseBytes?: boolean | undefined} | undefined, meta: M) => T; // client + deserialize: (data: Uint8Array, opts: {reuseBytes?: boolean} | undefined, meta: M) => T; // client }; export type ResponseMetadataCodec = {