From 1c28ce3dd3f50d3e099e28eb53b44c2a3fd9ce2d Mon Sep 17 00:00:00 2001 From: pk910 Date: Thu, 19 Feb 2026 18:51:51 +0100 Subject: [PATCH 1/2] use gloas go-eth2-client --- go.mod | 22 +++++++++++++--------- go.sum | 44 ++++++++++++++++++++++++-------------------- 2 files changed, 37 insertions(+), 29 deletions(-) diff --git a/go.mod b/go.mod index 5ea0d901..fa73c780 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( github.com/urfave/negroni v1.0.0 github.com/wealdtech/go-eth2-types/v2 v2.8.2 github.com/wealdtech/go-eth2-util v1.8.2 - golang.org/x/text v0.28.0 + golang.org/x/text v0.29.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -43,9 +43,11 @@ require ( require ( github.com/KyleBanks/depth v1.2.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/OffchainLabs/go-bitfield v0.0.0-20251031151322-f427d04d8506 // indirect github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.20.0 // indirect + github.com/casbin/govaluate v1.8.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/consensys/gnark-crypto v0.19.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect @@ -75,7 +77,7 @@ require ( github.com/itchyny/timefmt-go v0.1.6 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/kilic/bls12-381 v0.1.0 // indirect - github.com/klauspost/cpuid/v2 v2.2.9 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/mailru/easyjson v0.9.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect @@ -84,7 +86,8 @@ require ( github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect - github.com/pk910/dynamic-ssz v0.0.5 // indirect + github.com/pk910/dynamic-ssz v1.2.1 // indirect + github.com/pk910/hashtree-bindings v0.0.1 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.66.1 // indirect @@ -106,20 +109,21 @@ require ( go.opentelemetry.io/otel/trace v1.37.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect - golang.org/x/crypto v0.41.0 // indirect + golang.org/x/crypto v0.42.0 // indirect golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect - golang.org/x/mod v0.26.0 // indirect - golang.org/x/net v0.43.0 // indirect - golang.org/x/sync v0.16.0 // indirect + golang.org/x/mod v0.28.0 // indirect + golang.org/x/net v0.44.0 // indirect + golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.36.0 // indirect golang.org/x/time v0.14.0 // indirect - golang.org/x/tools v0.35.0 // indirect + golang.org/x/tools v0.37.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect google.golang.org/protobuf v1.36.8 // indirect - gopkg.in/Knetic/govaluate.v3 v3.0.0 // indirect gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect modernc.org/libc v1.66.3 // indirect modernc.org/mathutil v1.7.1 // indirect modernc.org/memory v1.11.0 // indirect modernc.org/sqlite v1.38.2 // indirect ) + +replace github.com/attestantio/go-eth2-client => github.com/pk910/go-eth2-client v0.0.0-20260219114320-6080c2df7e2f diff --git a/go.sum b/go.sum index 34813a1c..1503cb91 100644 --- a/go.sum +++ b/go.sum @@ -6,18 +6,20 @@ github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/OffchainLabs/go-bitfield v0.0.0-20251031151322-f427d04d8506 h1:d/SJkN8/9Ca+1YmuDiUJxAiV4w/a9S8NcsG7GMQSrVI= +github.com/OffchainLabs/go-bitfield v0.0.0-20251031151322-f427d04d8506/go.mod h1:6TZI4FU6zT8x6ZfWa1J8YQ2NgW0wLV/W3fHRca8ISBo= github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 h1:1zYrtlhrZ6/b6SAjLSfKzWtdgqK0U+HtH/VcBWh1BaU= github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0= github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU= -github.com/attestantio/go-eth2-client v0.27.1 h1:g7bm+gG/p+gfzYdEuxuAepVWYb8EO+2KojV5/Lo2BxM= -github.com/attestantio/go-eth2-client v0.27.1/go.mod h1:fvULSL9WtNskkOB4i+Yyr6BKpNHXvmpGZj9969fCrfY= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU= github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/casbin/govaluate v1.8.0 h1:1dUaV/I0LFP2tcY1uNQEb6wBCbp8GMTcC/zhwQDWvZo= +github.com/casbin/govaluate v1.8.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A= github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= @@ -186,8 +188,8 @@ github.com/kilic/bls12-381 v0.1.0 h1:encrdjqKMEvabVQ7qYOKu1OvhqpK4s47wDYtNiPtlp4 github.com/kilic/bls12-381 v0.1.0/go.mod h1:vDTTHJONJ6G+P2R74EhnyotQDTliQDnFEwhdmfzw1ig= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= -github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= -github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -246,8 +248,12 @@ github.com/pion/transport/v2 v2.2.1 h1:7qYnCBlpgSJNYMbLCKuSY9KbQdBFoETvPNETv0y4N github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g= github.com/pion/transport/v3 v3.0.1 h1:gDTlPJwROfSfz6QfSi0ZmeCSkFcnWWiiR9ES0ouANiM= github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= -github.com/pk910/dynamic-ssz v0.0.5 h1:VP9heGYUwzlpyhk28P2nCAzhvGsePJOOOO5vQMDh2qQ= -github.com/pk910/dynamic-ssz v0.0.5/go.mod h1:b6CrLaB2X7pYA+OSEEbkgXDEcRnjLOZIxZTsMuO/Y9c= +github.com/pk910/dynamic-ssz v1.2.1 h1:84eNMiiOYDiNC2Y1m5A/UtIPs6u/9SsvG4RVSBRGE5U= +github.com/pk910/dynamic-ssz v1.2.1/go.mod h1:HXRWLNcgj3DL65Kznrb+RdL3DEKw2JBZ/6crooqGoII= +github.com/pk910/go-eth2-client v0.0.0-20260219114320-6080c2df7e2f h1:OjX1YemePnLes4JqeDFIJOZN9YPCc4R6cyvyXFtVA6c= +github.com/pk910/go-eth2-client v0.0.0-20260219114320-6080c2df7e2f/go.mod h1:8fpxrIBBVbOcVG3vcHe5ubOHIeqW3N5t7kS4oU5EeJU= +github.com/pk910/hashtree-bindings v0.0.1 h1:Sw+UlPlrBle4LUg04kqLFybVQcfmamwKL1QsrR3GU0g= +github.com/pk910/hashtree-bindings v0.0.1/go.mod h1:eayIpxMFkWzMsydESu/5bV8wglZzSE/c9mq6DQdn204= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -352,25 +358,25 @@ go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= -golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= +golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= +golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg= -golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= +golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= +golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191116160921-f9c825593386/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= +golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -393,22 +399,20 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= -golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= +golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= +golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= -gopkg.in/Knetic/govaluate.v3 v3.0.0 h1:18mUyIt4ZlRlFZAAfVetz4/rzlJs9yhN+U02F4u1AOc= -gopkg.in/Knetic/govaluate.v3 v3.0.0/go.mod h1:csKLBORsPbafmSCGTEh3U7Ozmsuq8ZSIlKk1bcqph0E= gopkg.in/cenkalti/backoff.v1 v1.1.0 h1:Arh75ttbsvlpVA7WtVpH4u9h6Zl46xuptxqLxPiSo4Y= gopkg.in/cenkalti/backoff.v1 v1.1.0/go.mod h1:J6Vskwqd+OMVJl8C33mmtxTBs2gyzfv7UDAkHu8BrjI= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 7534da98f1967e66ca1b4118188aaf3c92a87093 Mon Sep 17 00:00:00 2001 From: pk910 Date: Thu, 19 Feb 2026 19:15:29 +0100 Subject: [PATCH 2/2] add basic gloas support --- pkg/clients/clients.go | 67 +++++-- pkg/clients/consensus/block.go | 68 ++++++- pkg/clients/consensus/block_utils.go | 32 ++- pkg/clients/consensus/blockcache.go | 20 +- pkg/clients/consensus/chainspec.go | 13 ++ pkg/clients/consensus/clientlogic.go | 70 ++++++- pkg/clients/consensus/rpc/beaconapi.go | 24 +++ pkg/clients/consensus/rpc/beaconstream.go | 80 +++++++- .../check_consensus_block_proposals/config.go | 1 + .../check_consensus_block_proposals/task.go | 182 ++++++++++++++---- 10 files changed, 481 insertions(+), 76 deletions(-) diff --git a/pkg/clients/clients.go b/pkg/clients/clients.go index e845612d..4d215bca 100644 --- a/pkg/clients/clients.go +++ b/pkg/clients/clients.go @@ -9,6 +9,7 @@ import ( "runtime/debug" "time" + "github.com/attestantio/go-eth2-client/spec" "github.com/ethereum/go-ethereum/common" "github.com/ethpandaops/assertoor/pkg/clients/consensus" "github.com/ethpandaops/assertoor/pkg/clients/execution" @@ -123,35 +124,61 @@ func (pool *ClientPool) processConsensusBlockNotification(poolClient *PoolClient } }() - subscription := poolClient.ConsensusClient.SubscribeBlockEvent(100) - defer subscription.Unsubscribe() + blockSubscription := poolClient.ConsensusClient.SubscribeBlockEvent(100) + defer blockSubscription.Unsubscribe() + + payloadSubscription := pool.consensusPool.GetBlockCache().SubscribePayloadEvent(100) + defer payloadSubscription.Unsubscribe() for { select { case <-pool.ctx.Done(): return - case block := <-subscription.Channel(): - versionedBlock := block.AwaitBlock(context.Background(), 2*time.Second) - if versionedBlock == nil { - pool.logger.Warnf("cl/el block notification failed: AwaitBlock timeout (client: %v, slot: %v, root: 0x%x)", poolClient.Config.Name, block.Slot, block.Root) - break - } + case block := <-blockSubscription.Channel(): + pool.notifyELBlockFromBeaconBlock(poolClient, block) + case block := <-payloadSubscription.Channel(): + pool.notifyELBlockFromPayload(poolClient, block) + } + } +} - hash, err := versionedBlock.ExecutionBlockHash() - if err != nil { - pool.logger.Warnf("cl/el block notification failed: %s (client: %v, slot: %v, root: 0x%x)", err, poolClient.Config.Name, block.Slot, block.Root) - break - } +func (pool *ClientPool) notifyELBlockFromBeaconBlock(poolClient *PoolClient, block *consensus.Block) { + versionedBlock := block.AwaitBlock(context.Background(), 2*time.Second) + if versionedBlock == nil { + pool.logger.Warnf("cl/el block notification failed: AwaitBlock timeout (client: %v, slot: %v, root: 0x%x)", poolClient.Config.Name, block.Slot, block.Root) + return + } - number, err := versionedBlock.ExecutionBlockNumber() - if err != nil { - pool.logger.Warnf("cl/el block notification failed: %s (client: %v, slot: %v, root: 0x%x)", err, poolClient.Config.Name, block.Slot, block.Root) - break - } + // For gloas+ blocks, EL info comes from the payload, not the block body + if versionedBlock.Version >= spec.DataVersionGloas { + return + } - poolClient.ExecutionClient.NotifyNewBlock(common.Hash(hash), number) - } + hash, err := versionedBlock.ExecutionBlockHash() + if err != nil { + pool.logger.Warnf("cl/el block notification failed: %s (client: %v, slot: %v, root: 0x%x)", err, poolClient.Config.Name, block.Slot, block.Root) + return + } + + number, err := versionedBlock.ExecutionBlockNumber() + if err != nil { + pool.logger.Warnf("cl/el block notification failed: %s (client: %v, slot: %v, root: 0x%x)", err, poolClient.Config.Name, block.Slot, block.Root) + return } + + poolClient.ExecutionClient.NotifyNewBlock(common.Hash(hash), number) +} + +func (pool *ClientPool) notifyELBlockFromPayload(poolClient *PoolClient, block *consensus.Block) { + payload := block.GetPayload() + if payload == nil || payload.Message == nil || payload.Message.Payload == nil { + return + } + + hash := common.Hash(payload.Message.Payload.BlockHash) + number := payload.Message.Payload.BlockNumber + + poolClient.ExecutionClient.NotifyNewBlock(hash, number) } func (pool *ClientPool) GetConsensusPool() *consensus.Pool { diff --git a/pkg/clients/consensus/block.go b/pkg/clients/consensus/block.go index b9b211f1..1f2cfc1f 100644 --- a/pkg/clients/consensus/block.go +++ b/pkg/clients/consensus/block.go @@ -7,20 +7,24 @@ import ( "time" "github.com/attestantio/go-eth2-client/spec" + "github.com/attestantio/go-eth2-client/spec/gloas" "github.com/attestantio/go-eth2-client/spec/phase0" ) type Block struct { - Root phase0.Root - Slot phase0.Slot - headerMutex sync.Mutex - headerChan chan bool - header *phase0.SignedBeaconBlockHeader - blockMutex sync.Mutex - blockChan chan bool - block *spec.VersionedSignedBeaconBlock - seenMutex sync.RWMutex - seenMap map[uint16]*Client + Root phase0.Root + Slot phase0.Slot + headerMutex sync.Mutex + headerChan chan bool + header *phase0.SignedBeaconBlockHeader + blockMutex sync.Mutex + blockChan chan bool + block *spec.VersionedSignedBeaconBlock + payloadMutex sync.Mutex + payloadChan chan bool + payload *gloas.SignedExecutionPayloadEnvelope + seenMutex sync.RWMutex + seenMap map[uint16]*Client } func (block *Block) GetSeenBy() []*Client { @@ -140,3 +144,47 @@ func (block *Block) EnsureBlock(loadBlock func() (*spec.VersionedSignedBeaconBlo return true, nil } + +// GetPayload returns the execution payload envelope if available. +func (block *Block) GetPayload() *gloas.SignedExecutionPayloadEnvelope { + return block.payload +} + +// AwaitPayload waits for the execution payload envelope to become available. +func (block *Block) AwaitPayload(ctx context.Context, timeout time.Duration) *gloas.SignedExecutionPayloadEnvelope { + if ctx == nil { + ctx = context.Background() + } + + select { + case <-block.payloadChan: + case <-time.After(timeout): + case <-ctx.Done(): + } + + return block.payload +} + +// EnsurePayload loads and sets the execution payload envelope if not already set. +func (block *Block) EnsurePayload(loadPayload func() (*gloas.SignedExecutionPayloadEnvelope, error)) (bool, error) { + if block.payload != nil { + return false, nil + } + + block.payloadMutex.Lock() + defer block.payloadMutex.Unlock() + + if block.payload != nil { + return false, nil + } + + payload, err := loadPayload() + if err != nil { + return false, err + } + + block.payload = payload + close(block.payloadChan) + + return true, nil +} diff --git a/pkg/clients/consensus/block_utils.go b/pkg/clients/consensus/block_utils.go index 52a352cc..461871f7 100644 --- a/pkg/clients/consensus/block_utils.go +++ b/pkg/clients/consensus/block_utils.go @@ -4,6 +4,7 @@ import ( "errors" "github.com/attestantio/go-eth2-client/spec" + "github.com/attestantio/go-eth2-client/spec/gloas" ) func GetExecutionExtraData(v *spec.VersionedSignedBeaconBlock) ([]byte, error) { @@ -23,15 +24,38 @@ func GetExecutionExtraData(v *spec.VersionedSignedBeaconBlock) ([]byte, error) { return v.Capella.Message.Body.ExecutionPayload.ExtraData, nil case spec.DataVersionDeneb: if v.Deneb == nil || v.Deneb.Message == nil || v.Deneb.Message.Body == nil || v.Deneb.Message.Body.ExecutionPayload == nil { - return nil, errors.New("no denb block") + return nil, errors.New("no deneb block") } return v.Deneb.Message.Body.ExecutionPayload.ExtraData, nil + case spec.DataVersionElectra: + if v.Electra == nil || v.Electra.Message == nil || v.Electra.Message.Body == nil || v.Electra.Message.Body.ExecutionPayload == nil { + return nil, errors.New("no electra block") + } + + return v.Electra.Message.Body.ExecutionPayload.ExtraData, nil + case spec.DataVersionFulu: + if v.Fulu == nil || v.Fulu.Message == nil || v.Fulu.Message.Body == nil || v.Fulu.Message.Body.ExecutionPayload == nil { + return nil, errors.New("no fulu block") + } + + return v.Fulu.Message.Body.ExecutionPayload.ExtraData, nil + case spec.DataVersionGloas: + return nil, errors.New("gloas extra data is in separate payload") default: return nil, errors.New("unknown version") } } +// GetPayloadExtraData returns the extra data from a gloas execution payload envelope. +func GetPayloadExtraData(payload *gloas.SignedExecutionPayloadEnvelope) ([]byte, error) { + if payload == nil || payload.Message == nil || payload.Message.Payload == nil { + return nil, errors.New("no payload") + } + + return payload.Message.Payload.ExtraData, nil +} + func GetBlockBody(v *spec.VersionedSignedBeaconBlock) any { //nolint:exhaustive // ignore switch v.Version { @@ -45,6 +69,12 @@ func GetBlockBody(v *spec.VersionedSignedBeaconBlock) any { return v.Capella case spec.DataVersionDeneb: return v.Deneb + case spec.DataVersionElectra: + return v.Electra + case spec.DataVersionFulu: + return v.Fulu + case spec.DataVersionGloas: + return v.Gloas default: return nil } diff --git a/pkg/clients/consensus/blockcache.go b/pkg/clients/consensus/blockcache.go index 48e5312d..1d0205c8 100644 --- a/pkg/clients/consensus/blockcache.go +++ b/pkg/clients/consensus/blockcache.go @@ -45,6 +45,7 @@ type BlockCache struct { maxSlotIdx int64 blockDispatcher Dispatcher[*Block] + payloadDispatcher Dispatcher[*Block] checkpointDispatcher Dispatcher[*FinalizedCheckpoint] wallclockEpochDispatcher Dispatcher[*ethwallclock.Epoch] wallclockSlotDispatcher Dispatcher[*ethwallclock.Slot] @@ -99,6 +100,14 @@ func (cache *BlockCache) notifyBlockReady(block *Block) { cache.blockDispatcher.Fire(block) } +func (cache *BlockCache) SubscribePayloadEvent(capacity int) *Subscription[*Block] { + return cache.payloadDispatcher.Subscribe(capacity) +} + +func (cache *BlockCache) notifyPayloadReady(block *Block) { + cache.payloadDispatcher.Fire(block) +} + func (cache *BlockCache) SetMinFollowDistance(followDistance uint64) { if followDistance > 10000 { followDistance = 10000 @@ -266,11 +275,12 @@ func (cache *BlockCache) AddBlock(root phase0.Root, slot phase0.Slot) (*Block, b } cacheBlock := &Block{ - Root: root, - Slot: slot, - seenMap: make(map[uint16]*Client), - headerChan: make(chan bool), - blockChan: make(chan bool), + Root: root, + Slot: slot, + seenMap: make(map[uint16]*Client), + headerChan: make(chan bool), + blockChan: make(chan bool), + payloadChan: make(chan bool), } cache.blockRootMap[root] = cacheBlock diff --git a/pkg/clients/consensus/chainspec.go b/pkg/clients/consensus/chainspec.go index 7de457cf..2004afac 100644 --- a/pkg/clients/consensus/chainspec.go +++ b/pkg/clients/consensus/chainspec.go @@ -25,11 +25,24 @@ type ChainSpec struct { BellatrixForkEpoch uint64 `yaml:"BELLATRIX_FORK_EPOCH"` CappellaForkVersion phase0.Version `yaml:"CAPELLA_FORK_VERSION"` CappellaForkEpoch uint64 `yaml:"CAPELLA_FORK_EPOCH"` + DenebForkEpoch uint64 `yaml:"DENEB_FORK_EPOCH"` + ElectraForkEpoch uint64 `yaml:"ELECTRA_FORK_EPOCH"` + FuluForkEpoch uint64 `yaml:"FULU_FORK_EPOCH"` + GloasForkEpoch uint64 `yaml:"GLOAS_FORK_EPOCH"` SecondsPerSlot time.Duration `yaml:"SECONDS_PER_SLOT"` SlotsPerEpoch uint64 `yaml:"SLOTS_PER_EPOCH"` MaxCommitteesPerSlot uint64 `yaml:"MAX_COMMITTEES_PER_SLOT"` } +// IsGloasActive returns true if the gloas fork is active at the given slot. +func (chain *ChainSpec) IsGloasActive(slot phase0.Slot) bool { + if chain.GloasForkEpoch == 0 || chain.SlotsPerEpoch == 0 { + return false + } + + return uint64(slot) >= chain.GloasForkEpoch*chain.SlotsPerEpoch +} + func (chain *ChainSpec) CheckMismatch(chain2 *ChainSpec) []string { mismatches := []string{} diff --git a/pkg/clients/consensus/clientlogic.go b/pkg/clients/consensus/clientlogic.go index 1a8bde2c..9aab87df 100644 --- a/pkg/clients/consensus/clientlogic.go +++ b/pkg/clients/consensus/clientlogic.go @@ -9,6 +9,7 @@ import ( v1 "github.com/attestantio/go-eth2-client/api/v1" "github.com/attestantio/go-eth2-client/spec" + "github.com/attestantio/go-eth2-client/spec/gloas" "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/ethpandaops/assertoor/pkg/clients/consensus/rpc" ) @@ -137,7 +138,7 @@ func (client *Client) runClientLogic() error { } // start event stream - blockStream := client.rpcClient.NewBlockStream(client.clientCtx, rpc.StreamBlockEvent|rpc.StreamFinalizedEvent) + blockStream := client.rpcClient.NewBlockStream(client.clientCtx, rpc.StreamBlockEvent|rpc.StreamFinalizedEvent|rpc.StreamExecutionPayloadEvent) defer blockStream.Close() // process events @@ -173,6 +174,14 @@ func (client *Client) runClientLogic() error { client.logger.Warnf("failed processing finalized event: %v", err) } } + + case rpc.StreamExecutionPayloadEvent: + if payloadEvent, ok := evt.Data.(*v1.ExecutionPayloadAvailableEvent); ok { + err := client.processPayloadEvent(payloadEvent) + if err != nil { + client.logger.Warnf("failed processing payload event: %v", err) + } + } } client.logger.Tracef("event (%v) processing time: %v ms", evt.Event, time.Since(now).Milliseconds()) @@ -220,6 +229,36 @@ func (client *Client) processFinalizedEvent(evt *v1.FinalizedCheckpointEvent) er return client.setFinalizedHead(evt.Epoch, evt.Block) } +func (client *Client) processPayloadEvent(evt *v1.ExecutionPayloadAvailableEvent) error { + cachedBlock := client.pool.blockCache.GetCachedBlockByRoot(evt.BlockRoot) + if cachedBlock == nil { + client.logger.Debugf("received payload event for unknown block [0x%x]", evt.BlockRoot) + return nil + } + + loaded, err := cachedBlock.EnsurePayload(func() (*gloas.SignedExecutionPayloadEnvelope, error) { + ctx, cancel := context.WithTimeout(client.clientCtx, 10*time.Second) + defer cancel() + + payload, err := client.rpcClient.GetExecutionPayloadByBlockroot(ctx, evt.BlockRoot) + if err != nil { + return nil, err + } + + return payload, nil + }) + if err != nil { + return fmt.Errorf("could not load payload for block [0x%x]: %w", evt.BlockRoot, err) + } + + if loaded { + client.logger.Infof("received execution payload for block %v [0x%x]", cachedBlock.Slot, evt.BlockRoot) + client.pool.blockCache.notifyPayloadReady(cachedBlock) + } + + return nil +} + func (client *Client) pollClientHead() error { ctx, cancel := context.WithTimeout(client.clientCtx, 10*time.Second) defer cancel() @@ -296,6 +335,12 @@ func (client *Client) processBlock(root phase0.Root, slot phase0.Slot, header *p if loaded { client.pool.blockCache.notifyBlockReady(cachedBlock) + + // For gloas+ blocks, also try to load payload (for polled/backfill blocks) + blockData := cachedBlock.GetBlock() + if blockData != nil && blockData.Version >= spec.DataVersionGloas { + go client.loadBlockPayload(cachedBlock) + } } client.headMutex.Lock() @@ -315,6 +360,29 @@ func (client *Client) processBlock(root phase0.Root, slot phase0.Slot, header *p return nil } +func (client *Client) loadBlockPayload(cachedBlock *Block) { + loaded, err := cachedBlock.EnsurePayload(func() (*gloas.SignedExecutionPayloadEnvelope, error) { + ctx, cancel := context.WithTimeout(client.clientCtx, 10*time.Second) + defer cancel() + + payload, err := client.rpcClient.GetExecutionPayloadByBlockroot(ctx, cachedBlock.Root) + if err != nil { + return nil, err + } + + return payload, nil + }) + if err != nil { + client.logger.Warnf("could not load payload for block %v [0x%x]: %v", cachedBlock.Slot, cachedBlock.Root, err) + return + } + + if loaded { + client.logger.Debugf("loaded execution payload for block %v [0x%x]", cachedBlock.Slot, cachedBlock.Root) + client.pool.blockCache.notifyPayloadReady(cachedBlock) + } +} + func (client *Client) setFinalizedHead(epoch phase0.Epoch, root phase0.Root) error { client.headMutex.Lock() diff --git a/pkg/clients/consensus/rpc/beaconapi.go b/pkg/clients/consensus/rpc/beaconapi.go index 934950a5..69c3975a 100644 --- a/pkg/clients/consensus/rpc/beaconapi.go +++ b/pkg/clients/consensus/rpc/beaconapi.go @@ -16,6 +16,7 @@ import ( "github.com/attestantio/go-eth2-client/http" "github.com/attestantio/go-eth2-client/spec" "github.com/attestantio/go-eth2-client/spec/capella" + "github.com/attestantio/go-eth2-client/spec/gloas" "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/rs/zerolog" "github.com/sirupsen/logrus" @@ -338,6 +339,29 @@ func (bc *BeaconClient) GetBlockHeaderBySlot(ctx context.Context, slot phase0.Sl return result.Data, nil } +func (bc *BeaconClient) GetExecutionPayloadByBlockroot(ctx context.Context, blockroot phase0.Root) (*gloas.SignedExecutionPayloadEnvelope, error) { + provider, isProvider := bc.clientSvc.(eth2client.ExecutionPayloadProvider) + if !isProvider { + return nil, fmt.Errorf("get execution payload not supported") + } + + result, err := provider.SignedExecutionPayloadEnvelope(ctx, &api.SignedExecutionPayloadEnvelopeOpts{ + Block: fmt.Sprintf("0x%x", blockroot), + Common: api.CommonOpts{ + Timeout: 0, + }, + }) + if err != nil { + if strings.HasPrefix(err.Error(), "GET failed with status 404") { + return nil, nil + } + + return nil, err + } + + return result.Data, nil +} + func (bc *BeaconClient) GetBlockBodyByBlockroot(ctx context.Context, blockroot phase0.Root) (*spec.VersionedSignedBeaconBlock, error) { provider, isProvider := bc.clientSvc.(eth2client.SignedBeaconBlockProvider) if !isProvider { diff --git a/pkg/clients/consensus/rpc/beaconstream.go b/pkg/clients/consensus/rpc/beaconstream.go index 28981e0f..f284a5b7 100644 --- a/pkg/clients/consensus/rpc/beaconstream.go +++ b/pkg/clients/consensus/rpc/beaconstream.go @@ -16,9 +16,10 @@ import ( ) const ( - StreamBlockEvent uint16 = 0x01 - StreamHeadEvent uint16 = 0x02 - StreamFinalizedEvent uint16 = 0x04 + StreamBlockEvent uint16 = 0x01 + StreamHeadEvent uint16 = 0x02 + StreamFinalizedEvent uint16 = 0x04 + StreamExecutionPayloadEvent uint16 = 0x08 ) type BeaconStreamEvent struct { @@ -63,7 +64,16 @@ func (bs *BeaconStream) startStream() { bs.running = false }() - stream := bs.subscribeStream(bs.client.endpoint, bs.events) + // Start advanced stream (execution_payload_available) in a separate goroutine + // if requested. This runs independently since CL clients may not support it yet. + if bs.events&StreamExecutionPayloadEvent > 0 { + go bs.runAdvancedStream() + } + + // Basic stream: block, head, finalized_checkpoint + basicEvents := bs.events &^ StreamExecutionPayloadEvent + + stream := bs.subscribeStream(bs.client.endpoint, basicEvents) if stream != nil { defer stream.Close() @@ -94,6 +104,43 @@ func (bs *BeaconStream) startStream() { } } +func (bs *BeaconStream) runAdvancedStream() { + for { + stream := bs.subscribeStream(bs.client.endpoint, StreamExecutionPayloadEvent) + if stream == nil { + return + } + + bs.runAdvancedStreamEvents(stream) + stream.Close() + + select { + case <-bs.ctx.Done(): + return + case <-time.After(5 * time.Second): + } + } +} + +func (bs *BeaconStream) runAdvancedStreamEvents(stream *eventstream.Stream) { + for { + select { + case <-bs.ctx.Done(): + return + case evt := <-stream.Events: + if evt.Event() == "execution_payload_available" { + bs.processExecutionPayloadAvailableEvent(evt) + } + case <-stream.Ready: + // advanced stream ready, nothing to signal + case err := <-stream.Errors: + logger.WithField("client", bs.client.name).Debugf("advanced beacon stream error: %v", err) + + return + } + } +} + func (bs *BeaconStream) subscribeStream(endpoint string, events uint16) *eventstream.Stream { var topics strings.Builder @@ -129,6 +176,16 @@ func (bs *BeaconStream) subscribeStream(endpoint string, events uint16) *eventst topicsCount++ } + if events&StreamExecutionPayloadEvent > 0 { + if topicsCount > 0 { + fmt.Fprintf(&topics, ",") + } + + fmt.Fprintf(&topics, "execution_payload_available") + + topicsCount++ + } + if topicsCount == 0 { return nil } @@ -207,6 +264,21 @@ func (bs *BeaconStream) processFinalizedEvent(evt eventsource.Event) { } } +func (bs *BeaconStream) processExecutionPayloadAvailableEvent(evt eventsource.Event) { + var parsed v1.ExecutionPayloadAvailableEvent + + err := json.Unmarshal([]byte(evt.Data()), &parsed) + if err != nil { + logger.WithField("client", bs.client.name).Warnf("beacon block stream failed to decode execution_payload_available event: %v", err) + return + } + + bs.EventChan <- &BeaconStreamEvent{ + Event: StreamExecutionPayloadEvent, + Data: &parsed, + } +} + func getRedactedURL(requrl string) string { var logurl string diff --git a/pkg/tasks/check_consensus_block_proposals/config.go b/pkg/tasks/check_consensus_block_proposals/config.go index 5b27dbd9..c9d89ba3 100644 --- a/pkg/tasks/check_consensus_block_proposals/config.go +++ b/pkg/tasks/check_consensus_block_proposals/config.go @@ -5,6 +5,7 @@ import "math/big" type Config struct { CheckLookback int `yaml:"checkLookback" json:"checkLookback" desc:"Number of slots to look back when checking for block proposals."` BlockCount int `yaml:"blockCount" json:"blockCount" desc:"Number of matching blocks required to pass the check."` + PayloadTimeout int `yaml:"payloadTimeout" json:"payloadTimeout" desc:"Timeout in seconds to wait for execution payload (gloas+). Default: 12"` GraffitiPattern string `yaml:"graffitiPattern" json:"graffitiPattern" desc:"Regex pattern to match block graffiti."` ValidatorNamePattern string `yaml:"validatorNamePattern" json:"validatorNamePattern" desc:"Regex pattern to match validator names."` ExtraDataPattern string `yaml:"extraDataPattern" json:"extraDataPattern" desc:"Regex pattern to match execution payload extra data."` diff --git a/pkg/tasks/check_consensus_block_proposals/task.go b/pkg/tasks/check_consensus_block_proposals/task.go index dfc88a2d..d8e35d09 100644 --- a/pkg/tasks/check_consensus_block_proposals/task.go +++ b/pkg/tasks/check_consensus_block_proposals/task.go @@ -10,6 +10,9 @@ import ( "time" "github.com/attestantio/go-eth2-client/spec" + "github.com/attestantio/go-eth2-client/spec/capella" + "github.com/attestantio/go-eth2-client/spec/electra" + "github.com/attestantio/go-eth2-client/spec/gloas" "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/ethereum/go-ethereum/common" "github.com/ethpandaops/assertoor/pkg/clients/consensus" @@ -214,6 +217,29 @@ func (t *Task) setMatchingBlocksOutput(blocks []*consensus.Block) { t.ctx.Outputs.SetVar("matchingBlockBodies", blockBodies) } +// needsPayload returns true if any payload-dependent filter is configured. +func (t *Task) needsPayload() bool { + return t.config.ExtraDataPattern != "" || + t.config.MinTransactionCount > 0 || + t.config.MinWithdrawalCount > 0 || + len(t.config.ExpectWithdrawals) > 0 || + t.config.MinDepositRequestCount > 0 || + len(t.config.ExpectDepositRequests) > 0 || + t.config.MinWithdrawalRequestCount > 0 || + len(t.config.ExpectWithdrawalRequests) > 0 || + t.config.MinConsolidationRequestCount > 0 || + len(t.config.ExpectConsolidationRequests) > 0 +} + +// getPayloadTimeout returns the configured payload timeout or the default of 12 seconds. +func (t *Task) getPayloadTimeout() time.Duration { + if t.config.PayloadTimeout > 0 { + return time.Duration(t.config.PayloadTimeout) * time.Second + } + + return 12 * time.Second +} + //nolint:gocyclo // ignore func (t *Task) checkBlock(ctx context.Context, block *consensus.Block) bool { blockData := block.AwaitBlock(ctx, 2*time.Second) @@ -222,6 +248,18 @@ func (t *Task) checkBlock(ctx context.Context, block *consensus.Block) bool { return false } + // For gloas+ blocks, load payload if any payload-dependent checks are configured + var payload *gloas.SignedExecutionPayloadEnvelope + + isGloas := blockData.Version >= spec.DataVersionGloas + if isGloas && t.needsPayload() { + payload = block.AwaitPayload(ctx, t.getPayloadTimeout()) + if payload == nil { + t.logger.Warnf("could not fetch payload for gloas block %v [0x%x]", block.Slot, block.Root) + return false + } + } + // check validator name if t.config.ValidatorNamePattern != "" && !t.checkBlockValidatorName(block, blockData) { return false @@ -233,7 +271,7 @@ func (t *Task) checkBlock(ctx context.Context, block *consensus.Block) bool { } // check extra data - if t.config.ExtraDataPattern != "" && !t.checkBlockExtraData(block, blockData) { + if t.config.ExtraDataPattern != "" && !t.checkBlockExtraData(block, blockData, payload) { return false } @@ -273,12 +311,12 @@ func (t *Task) checkBlock(ctx context.Context, block *consensus.Block) bool { } // check withdrawal count - if (t.config.MinWithdrawalCount > 0 || len(t.config.ExpectWithdrawals) > 0) && !t.checkBlockWithdrawals(block, blockData) { + if (t.config.MinWithdrawalCount > 0 || len(t.config.ExpectWithdrawals) > 0) && !t.checkBlockWithdrawals(block, blockData, payload) { return false } // check transaction count - if t.config.MinTransactionCount > 0 && !t.checkBlockTransactions(block, blockData) { + if t.config.MinTransactionCount > 0 && !t.checkBlockTransactions(block, blockData, payload) { return false } @@ -288,17 +326,17 @@ func (t *Task) checkBlock(ctx context.Context, block *consensus.Block) bool { } // check deposit request count - if (t.config.MinDepositRequestCount > 0 || len(t.config.ExpectDepositRequests) > 0) && !t.checkBlockDepositRequests(block, blockData) { + if (t.config.MinDepositRequestCount > 0 || len(t.config.ExpectDepositRequests) > 0) && !t.checkBlockDepositRequests(block, blockData, payload) { return false } // check withdrawal request count - if (t.config.MinWithdrawalRequestCount > 0 || len(t.config.ExpectWithdrawalRequests) > 0) && !t.checkBlockWithdrawalRequests(block, blockData) { + if (t.config.MinWithdrawalRequestCount > 0 || len(t.config.ExpectWithdrawalRequests) > 0) && !t.checkBlockWithdrawalRequests(block, blockData, payload) { return false } // check consolidation request count - if (t.config.MinConsolidationRequestCount > 0 || len(t.config.ExpectConsolidationRequests) > 0) && !t.checkBlockConsolidationRequests(block, blockData) { + if (t.config.MinConsolidationRequestCount > 0 || len(t.config.ExpectConsolidationRequests) > 0) && !t.checkBlockConsolidationRequests(block, blockData, payload) { return false } @@ -349,8 +387,17 @@ func (t *Task) checkBlockValidatorName(block *consensus.Block, blockData *spec.V return true } -func (t *Task) checkBlockExtraData(block *consensus.Block, blockData *spec.VersionedSignedBeaconBlock) bool { - extraData, err := consensus.GetExecutionExtraData(blockData) +func (t *Task) checkBlockExtraData(block *consensus.Block, blockData *spec.VersionedSignedBeaconBlock, payload *gloas.SignedExecutionPayloadEnvelope) bool { + var extraData []byte + + var err error + + if blockData.Version >= spec.DataVersionGloas { + extraData, err = consensus.GetPayloadExtraData(payload) + } else { + extraData, err = consensus.GetExecutionExtraData(blockData) + } + if err != nil { t.logger.Warnf("could not get extra data for block %v [0x%x]: %v", block.Slot, block.Root, err) return false @@ -636,11 +683,24 @@ func (t *Task) checkBlockBlsChanges(block *consensus.Block, blockData *spec.Vers return true } -func (t *Task) checkBlockWithdrawals(block *consensus.Block, blockData *spec.VersionedSignedBeaconBlock) bool { - withdrawals, err := blockData.Withdrawals() - if err != nil { - t.logger.Warnf("could not get withdrawals for block %v [0x%x]: %v", block.Slot, block.Root, err) - return false +func (t *Task) checkBlockWithdrawals(block *consensus.Block, blockData *spec.VersionedSignedBeaconBlock, payload *gloas.SignedExecutionPayloadEnvelope) bool { + var withdrawals []*capella.Withdrawal + + if blockData.Version >= spec.DataVersionGloas { + if payload == nil || payload.Message == nil || payload.Message.Payload == nil { + t.logger.Warnf("could not get withdrawals for gloas block %v [0x%x]: no payload", block.Slot, block.Root) + return false + } + + withdrawals = payload.Message.Payload.Withdrawals + } else { + var err error + + withdrawals, err = blockData.Withdrawals() + if err != nil { + t.logger.Warnf("could not get withdrawals for block %v [0x%x]: %v", block.Slot, block.Root, err) + return false + } } if len(withdrawals) < t.config.MinWithdrawalCount { @@ -694,15 +754,28 @@ func (t *Task) checkBlockWithdrawals(block *consensus.Block, blockData *spec.Ver return true } -func (t *Task) checkBlockTransactions(block *consensus.Block, blockData *spec.VersionedSignedBeaconBlock) bool { - transactions, err := blockData.ExecutionTransactions() - if err != nil { - t.logger.Warnf("could not get transactions for block %v [0x%x]: %v", block.Slot, block.Root, err) - return false +func (t *Task) checkBlockTransactions(block *consensus.Block, blockData *spec.VersionedSignedBeaconBlock, payload *gloas.SignedExecutionPayloadEnvelope) bool { + var txCount int + + if blockData.Version >= spec.DataVersionGloas { + if payload == nil || payload.Message == nil || payload.Message.Payload == nil { + t.logger.Warnf("could not get transactions for gloas block %v [0x%x]: no payload", block.Slot, block.Root) + return false + } + + txCount = len(payload.Message.Payload.Transactions) + } else { + transactions, err := blockData.ExecutionTransactions() + if err != nil { + t.logger.Warnf("could not get transactions for block %v [0x%x]: %v", block.Slot, block.Root, err) + return false + } + + txCount = len(transactions) } - if len(transactions) < t.config.MinTransactionCount { - t.logger.Infof("check failed for block %v [0x%x]: not enough transactions (want: >= %v, have: %v)", block.Slot, block.Root, t.config.MinTransactionCount, len(transactions)) + if txCount < t.config.MinTransactionCount { + t.logger.Infof("check failed for block %v [0x%x]: not enough transactions (want: >= %v, have: %v)", block.Slot, block.Root, t.config.MinTransactionCount, txCount) return false } @@ -724,11 +797,24 @@ func (t *Task) checkBlockBlobs(block *consensus.Block, blockData *spec.Versioned return true } -func (t *Task) checkBlockDepositRequests(block *consensus.Block, blockData *spec.VersionedSignedBeaconBlock) bool { - executionRequests, err := blockData.ExecutionRequests() - if err != nil { - t.logger.Warnf("could not get execution requests for block %v [0x%x]: %v", block.Slot, block.Root, err) - return false +func (t *Task) checkBlockDepositRequests(block *consensus.Block, blockData *spec.VersionedSignedBeaconBlock, payload *gloas.SignedExecutionPayloadEnvelope) bool { + var executionRequests *electra.ExecutionRequests + + if blockData.Version >= spec.DataVersionGloas { + if payload == nil || payload.Message == nil || payload.Message.ExecutionRequests == nil { + t.logger.Warnf("could not get execution requests for gloas block %v [0x%x]: no payload", block.Slot, block.Root) + return false + } + + executionRequests = payload.Message.ExecutionRequests + } else { + var err error + + executionRequests, err = blockData.ExecutionRequests() + if err != nil { + t.logger.Warnf("could not get execution requests for block %v [0x%x]: %v", block.Slot, block.Root, err) + return false + } } depositRequests := executionRequests.Deposits @@ -774,11 +860,24 @@ func (t *Task) checkBlockDepositRequests(block *consensus.Block, blockData *spec return true } -func (t *Task) checkBlockWithdrawalRequests(block *consensus.Block, blockData *spec.VersionedSignedBeaconBlock) bool { - executionRequests, err := blockData.ExecutionRequests() - if err != nil { - t.logger.Warnf("could not get execution requests for block %v [0x%x]: %v", block.Slot, block.Root, err) - return false +func (t *Task) checkBlockWithdrawalRequests(block *consensus.Block, blockData *spec.VersionedSignedBeaconBlock, payload *gloas.SignedExecutionPayloadEnvelope) bool { + var executionRequests *electra.ExecutionRequests + + if blockData.Version >= spec.DataVersionGloas { + if payload == nil || payload.Message == nil || payload.Message.ExecutionRequests == nil { + t.logger.Warnf("could not get execution requests for gloas block %v [0x%x]: no payload", block.Slot, block.Root) + return false + } + + executionRequests = payload.Message.ExecutionRequests + } else { + var err error + + executionRequests, err = blockData.ExecutionRequests() + if err != nil { + t.logger.Warnf("could not get execution requests for block %v [0x%x]: %v", block.Slot, block.Root, err) + return false + } } withdrawalRequests := executionRequests.Withdrawals @@ -828,11 +927,24 @@ func (t *Task) checkBlockWithdrawalRequests(block *consensus.Block, blockData *s return true } -func (t *Task) checkBlockConsolidationRequests(block *consensus.Block, blockData *spec.VersionedSignedBeaconBlock) bool { - executionRequests, err := blockData.ExecutionRequests() - if err != nil { - t.logger.Warnf("could not get execution requests for block %v [0x%x]: %v", block.Slot, block.Root, err) - return false +func (t *Task) checkBlockConsolidationRequests(block *consensus.Block, blockData *spec.VersionedSignedBeaconBlock, payload *gloas.SignedExecutionPayloadEnvelope) bool { + var executionRequests *electra.ExecutionRequests + + if blockData.Version >= spec.DataVersionGloas { + if payload == nil || payload.Message == nil || payload.Message.ExecutionRequests == nil { + t.logger.Warnf("could not get execution requests for gloas block %v [0x%x]: no payload", block.Slot, block.Root) + return false + } + + executionRequests = payload.Message.ExecutionRequests + } else { + var err error + + executionRequests, err = blockData.ExecutionRequests() + if err != nil { + t.logger.Warnf("could not get execution requests for block %v [0x%x]: %v", block.Slot, block.Root, err) + return false + } } consolidationRequests := executionRequests.Consolidations