PageSourceSearch

https://ethpandaops.io/assets/js/5a84f7f0.e6334e46.js

js ethpandaops.io collected 2026-10-03 21:47:58 UTC 16,016 bytes, 1 lines download raw bytes

1"use strict";(globalThis.webpackChunkpublic_docs=globalThis.webpackChunkpublic_docs||[]).push([[5846],{4617(e,t,i){i.r(t),i.d(t,{assets:()=>h,contentTitle:()=>c,default:()=>p,frontMatter:()=>r,metadata:()=>s,toc:()=>d});var s=i(69648),n=i(74848),a=i(28453),o=i(66362),l=i(30316);const r={slug:"hoodi-attestation-packing",title:"Electra on Hoodi: Attestation Packing",authors:["samcm"],description:"Analysis of the Hoodi Electra fork and its impact on attestation packing.",tags:["xatu","consensus-layer","hoodi","electra"],image:"/img/blog/hoodi-attestations.jpg",relatedLinks:[{name:"EIP-7549",url:"https://eips.ethereum.org/EIPS/eip-7549"},{name:"Prysm Analysis",url:"https://hackmd.io/@ttsao/attestation-packing-pectra"},{name:"Jupyter Notebook",url:"https://github.com/ethpandaops/xatu-data/blob/e9b7ef41ada2cbbb44d790994614a9aa302512fc/analysis/attestations/packing.ipynb"}]},c=void 0,h={authorsImageUrls:[void 0]},d=[{value:"Background",id:"background",level:2},{value:"Before Electra",id:"before-electra",level:3},{value:"After Electra",id:"after-electra",level:3},{value:"Analysis",id:"analysis",level:2},{value:"Methodology",id:"methodology",level:3},{value:"Results",id:"results",level:3},{value:"Unique Validators Per Block",id:"unique-validators-per-block",level:4},{value:"Fresh Attestations",id:"fresh-attestations",level:4},{value:"Inclusion Distance",id:"inclusion-distance",level:4},{value:"Optimal Inclusion Distance",id:"optimal-inclusion-distance",level:4},{value:"Conclusion",id:"conclusion",level:3}];function u(e){const t={a:"a",blockquote:"blockquote",code:"code",em:"em",h2:"h2",h3:"h3",h4:"h4",li:"li",p:"p",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsxs)(t.p,{children:["Last week, the newly established Hoodi network successfully activated its Electra fork, significantly changing how attestations are packed into beacon blocks, as detailed in ",(0,n.jsx)(t.a,{href:"https://eips.ethereum.org/EIPS/eip-7549",children:"EIP-7549"}),". This post recaps how attestation aggregation worked previously and how the recent fork changes things, before diving into some analysis."]}),"\n",(0,n.jsx)(o.A,{children:(0,n.jsxs)(t.p,{children:["Check out Prysm's Terence's post for another detailed analysis of ",(0,n.jsx)(t.a,{href:"https://hackmd.io/@ttsao/attestation-packing-pectra",children:"Attestation performance in Hoodi"}),"."]})}
1),"\n",(0,n.jsx)(t.h2,{id:"background",children:"Background"}),"\n",(0,n.jsx)(t.h3,{id:"before-electra",children:"Before Electra"}),"\n",(0,n.jsxs)(t.p,{children:["Previously, attestations included the ",(0,n.jsx)(t.code,{children:"committee_index"})," within the signed data."]}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"Attestations could only be aggregated within their specific committee, limiting overall aggregation efficiency."}),"\n",(0,n.jsx)(t.li,{children:"Identical votes from different committees produced distinct signatures."}),"\n"]}),"\n",(0,n.jsx)(t.h3,{id:"after-electra",children:"After Electra"}),"\n",(0,n.jsxs)(t.p,{children:["With EIP-7549, the attestation structure was changed by moving the ",(0,n.jsx)(t.code,{children:"committee_index"})," field ",(0,n.jsx)(t.strong,{children:"outside"})," the signed data."]}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"Identical votes across different committees can now be aggregated, drastically cutting down on unique attestations."}),"\n",(0,n.jsx)(t.li,{children:"Significant efficiency improvements in signature verification e.g., in a network with 262,144 validators, attestations required for a two-thirds majority fell dramatically from approximately 1,366 to just around 22."}),"\n"]}),"\n",(0,n.jsx)(t.p,{children:"While these changes offer great potential, there is also a risk that the changes will negatively impact performance. With these changes outlined, let\u2019s explore how Electra unfolded on Hoodi."}),"\n",(0,n.jsx)(t.h2,{id:"analysis",children:"Analysis"}),"\n",(0,n.jsx)(o.A,{children:(0,n.jsxs)(t.p,{children:["The raw Jupyter notebook used to generate the analysis below is available ",(0,n.jsx)(t.a,{href:"https://github.com/ethpandaops/xatu-data/blob/e9b7ef41ada2cbbb44d790994614a9aa302512fc/analysis/attestations/packing.ipynb",children:"here"}),"."]})}),"\n",(0,n.jsx)(t.h3,{id:"methodology",children:"Methodology"}),"\n",(0,n.jsx)(t.p,{children:"The following parameters were used for the analysis:"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["2x 6 hour time periods:","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["Pre-Electra: ",(0,n.jsx)(t.code,{children:"2025-03-25T12:10:00Z"})," to ",(0,n.jsx)(t.code,{children:"2025-03-25T18:10:00Z"})]}),"\n",(0,n.jsxs)(t.li,{children:["Post-Electra: ",(0,n.jsx)(t.code,{children:"2025-03-27T12:10:00Z"})," to ",(0,n.jsx)(t.code,{children:"2025-03-27T18:10:00Z"})]}),"\n"]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["Data was collected from the Hoodi network using ",(0,n.jsx)(t.a,{href:"/data/xatu/",children:"Xatu"})," (keep an eye out for public Hoodi data soon!)."]}),"\n",(0,n.jsxs)(t.li,{children:["The analysis looks at canonical beacon blocks and inspected the attestations that were included in them.","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"This analysis examines attestation packing efficiency (and by proxy aggregation performance), rather than evaluating the correctness of attestation votes."}),"\n"]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["We naively assume that client teams are running the client they develop.","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"Nimbus keys, managed by the ethPandaOps team during the analysis period, exclusively utilized the Nimbus beacon node client."}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.li,{children:"The ethPandaOps validator keys use multiple beacon node clients."}),"\n"]}),"\n",(0,n.jsx)(t.h3,{id:"results",children:"Results"}),"\n",(0,n.jsx)(t.h4,{id:"unique-validators-per-block",children:"Unique Validators Per Block"}),"\n",(0,n.jsxs)(t.p,{children:["Our first metric examines the ",(0,n.jsx)(t.strong,{children:"unique count of validators"})," included per block aggregated across all blocks during the two time periods."]}),"\n",(0,n.jsx)(t.p,{children:"With the Electra fork, we're expecting to see a much higher number of unique validators per block as older attestations can now be included. Note that this metric includes stale attestations by design."}),"\n",(0,n.jsx)(l.A,{alt:"Unique Validators Per Block",src:"/img/blog/all-unique-validators-per-block.jpg"}),"\n",(0,n.jsx)(t.p,{children:"Our analysis confirms a notable increase in unique validator indices per block, with some blocks including as many as 200,000 unique validators."}),"\n",(0,n.jsx)(t.h4,{id:"fresh-attestations",children:"Fresh Attestations"}),"\n",(0,n.jsxs)(t.p,{children:["The second metric assesses the number of fresh attestations per block. A fresh attestation is defined as a validator vote that has not been seen in any previous block. As attestations land in beacon blocks ",(0,n.jsx)(t.em,{children:"aggregated"}),", it's inevitable that some stale attestations will be included as overlaps can occur. However, we want to include as many fresh attestations as possible."]}),"\n",(0,n.jsx)(l.A,{alt:"Fresh Attestations",src:"/img/blog/all-first-seen-attestations.jpg"}),"\n",(0,n.jsx)(t.p,{children:"The analysis confirms a clear and substantial increase in capacity due to EIP-7549. With 1.08 million validators on Hoodi, approximately 33.7k attestations can occur per slot. The 80th percentile of Electra blocks aligns closely with this number, while a notable jump to approximately 60k attestations at the 90th percentile occurs--presumably after a missed slot."}),"\n",(0,n.jsx)(t.p,{children:"Surprisingly, a few blocks included as many as 120k fresh attestations. While this increased capacity is great, it's also a sign that the network may be experiencing issues. Statistically, the chance of observing four consecutive missed slots at 90% participation is around 1 in 10,000. Given our analysis covers only 3,276 blocks, it is unlikely we observed such an event."}),"\n",(0,n.jsx)(l.A,{alt:"Missed Slots",src:"/img/blog/missed-slots.jpg"}),"\n",(0,n.jsxs)(t.p,{children:["Confirming that we didn't see any blocks with 4 missed slots in a row, we can now ask the question: ",(0,n.jsx)(t.strong,{children:"why are block proposers ever given the chance to include fresh attestions older than 1, 2 or 3 missed slots?"})]}),"\n",(0,n.jsx)(t.h4,{id:"inclusion-distance",children:"Inclusion Distance"}),"\n",(0,n.jsx)(t.p,{children:"Inclusion distance measures the number of slots between the attestation\u2019s creation and its inclusion in a block. In a perfect world, the inclusion distance should be one slot."}),"\n",(0,n.jsx)(l.A,{alt:"Inclusion Distance",src:"/img/blog/all-inclusion-delay.jpg"}),"\n",(0,n.jsx)(t.p,{children:"The analysis reveals Nimbus client blocks experienced notably increased inclusion distances post-Electra, suggesting potential improvements in packing strategies."}),"\n",(0,n.jsx)(t.h4,{id:"optimal-inclusion-distance",children:"Optimal Inclusion Distance"}),"\n",(0,n.jsx)(t.p,{children:"The Optimal Inclusion Distance metric counts the number of attestations in the block with an inclusion distance of just one slot, highlighting the ability of block proposers to capture the most recent attestations."}),"\n",(0,n.jsxs)(t.blockquote,{children:["\n",(0,n.jsxs)(t.p,{children:["Note: this is slightly different to ",(0,n.jsx)(t.a,{href:"https://github.com/wealdtech/ethdo/blob/25a5bd917f55c1802a5bd924cba98bc56bf82c9b/cmd/epoch/summary/process.go#L381",children:"ethdo's"})," ",(0,n.jsx)(t.em,{children:"head timely"})," metric which additionally looks at the correctness of the vote."]}),"\n"]}),"\n",(0,n.jsx)(l.A,{alt:"Optimal Inclusion Distance",src:"/img/blog/head-timely-vali
1dators.jpg"}),"\n",(0,n.jsx)(t.p,{children:"It appears that Nimbus experienced a larger regression than other clients - warranting further investigation. Grandine is also seemingly performing worse that others, although the change before/after the fork is not as large."}),"\n",(0,n.jsx)(l.A,{alt:"Head Timely Nimbus",src:"/img/blog/head-timely-nimbus.jpg"}),"\n",(0,n.jsx)(t.p,{children:"Diving in to just Nimbus produced blocks, we can see that around 40% of them failed to include any of the most recent attestations. This outlier hints that there may be room for improvement with the Nimbus attestation packing strategy."}),"\n",(0,n.jsx)(l.A,{alt:"Head Timely Ex. Nimbus",src:"/img/blog/head-timely-no-nimbus.jpg"}),"\n",(0,n.jsx)(t.p,{children:"Looking at the same metric at the network level while excluding Nimbus, we see that peformance is still around 6% worse."}),"\n",(0,n.jsxs)(t.p,{children:["This is somewhat concerning. It's hard to attribute this to a drop in network participation, which remained around 90% ",(0,n.jsx)(t.a,{href:"https://dora.hoodi.ethpandaops.io/epoch/1800",children:"before"})," and ",(0,n.jsx)(t.a,{href:"https://dora.hoodi.ethpandaops.io/epoch/2250",children:"after"})," the fork. This may hint at underlying inefficiencies with attestation aggregation across the network before they get to the block proposer, but is out of scope for this analysis. It's also very likely that clients are still fine tuning their attestation packing strategies, and we expect to see this metric improve over time, particularly before Electra hits mainnet."]}),"\n",(0,n.jsx)(t.h3,{id:"conclusion",children:"Conclusion"}),"\n",(0,n.jsx)(t.p,{children:"The Electra fork has enhanced the capacity of attestations being included into beacon blocks. However, these advancements highlight the increased importance of efficient aggregation and proactive packing strategies."}),"\n",(0,n.jsxs)(t.p,{children:["While we observed some mixed results, the changes introduced in ",(0,n.jsx)(t.a,{href:"https://eips.ethereum.org/EIPS/eip-7549",children:"EIP-7549"})," are promising, and we expect the changes to be a net positive for the beacon chain in the long term, especially in times of turbulence."]})]})}function p(e={}){const{wrapper:t}={...(0,a.R)(),...e.components};return t?(0,n.jsx)(t,{...e,children:(0,n.jsx)(u,{...e})}):u(e)}},28453(e,t,i){i.d(t,{R:()=>o,x:()=>l});var s=i(96540);const n={},a=s.createContext(n);function o(e){const t=s.useContext(a);return s.useMemo(function(){return"function"==typeof e?e(t):{...t,...e}},[t,e])}function l(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(n):e.components||n:o(e.components),s.createElement(a.Provider,{value:t},e.children)}},66362(e,t,i){i.d(t,{A:()=>o});i(96540);const s={alert:"alert_dmjp",alertIcon:"alertIcon_W31s",alertContent:"alertContent_sQ4d","alert--info":"alert--info_mi3q","alert--warning":"alert--warning_zHeT","alert--danger":"alert--danger_mlzQ","alert--success":"alert--success_m5Z0"};var n=i(34164),a=i(74848);function o({children:e,type:t="info"}){return(0,a.jsxs)("div",{className:(0,n.A)(s.alert,s[`alert--${t}`]),children:[(0,a.jsxs)("div",{className:s.alertIcon,children:["info"===t&&(0,a.jsx)("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor",children:(0,a.jsx)("path",{d:"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 15c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1s1 .45 1 1v4c0 .55-.45 1-1 1zm1-8h-2V7h2v2z"})}),"warning"===t&&(0,a.jsx)("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor",children:(0,a.jsx)("path",{d:"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15h2v-2h-2v2zm0-4h2V7h-2v6z"})}),"danger"===t&&(0,a.jsx)("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor",children:(0,a.jsx)("path",{d:"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 11c-.55 0-1-.45-1-1V8c0-.55.45-1 1-1s1 .45 1 1v4c0 .55-.45 1-1 1zm1 4h-2v-2h2v2z"})}),"success"===t&&(0,a.jsx)("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"
1currentColor",children:(0,a.jsx)("path",{d:"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"})})]}),(0,a.jsx)("div",{className:s.alertContent,children:e})]})}},69648(e){e.exports=JSON.parse('{"permalink":"/posts/hoodi-attestation-packing","source":"@site/blog/2025-04-02-hoodi-attestation-packing/index.md","title":"Electra on Hoodi: Attestation Packing","description":"Analysis of the Hoodi Electra fork and its impact on attestation packing.","date":"2025-04-02T00:00:00.000Z","tags":[{"inline":true,"label":"xatu","permalink":"/posts/tags/xatu"},{"inline":true,"label":"consensus-layer","permalink":"/posts/tags/consensus-layer"},{"inline":true,"label":"hoodi","permalink":"/posts/tags/hoodi"},{"inline":true,"label":"electra","permalink":"/posts/tags/electra"}],"readingTime":5.59,"hasTruncateMarker":false,"authors":[{"name":"samcm","title":"DevOps Engineer","bio":"DevOps Engineer","url":"https://github.com/samcm","github":"https://github.com/samcm","twitter":"https://x.com/samcmAU","imageURL":"/img/team/samcm.jpeg","key":"samcm","page":null}],"frontMatter":{"slug":"hoodi-attestation-packing","title":"Electra on Hoodi: Attestation Packing","authors":["samcm"],"description":"Analysis of the Hoodi Electra fork and its impact on attestation packing.","tags":["xatu","consensus-layer","hoodi","electra"],"image":"/img/blog/hoodi-attestations.jpg","relatedLinks":[{"name":"EIP-7549","url":"https://eips.ethereum.org/EIPS/eip-7549"},{"name":"Prysm Analysis","url":"https://hackmd.io/@ttsao/attestation-packing-pectra"},{"name":"Jupyter Notebook","url":"https://github.com/ethpandaops/xatu-data/blob/e9b7ef41ada2cbbb44d790994614a9aa302512fc/analysis/attestations/packing.ipynb"}]},"unlisted":false,"prevItem":{"title":"Pectra Mainnet Checklist","permalink":"/posts/pectra-mainnet-checklist"},"nextItem":{"title":"EthPandaOps Pivots to GitChain","permalink":"/posts/april-fools"}}')}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.