diff --git a/clients/consensus/chainspec.go b/clients/consensus/chainspec.go index e5d3914..43311d6 100644 --- a/clients/consensus/chainspec.go +++ b/clients/consensus/chainspec.go @@ -51,6 +51,11 @@ type ChainSpec struct { SyncCommitteeSize uint64 `yaml:"SYNC_COMMITTEE_SIZE"` DepositContractAddress []byte `yaml:"DEPOSIT_CONTRACT_ADDRESS"` + // EIP7594: PeerDAS + NumberOfColumns *uint64 `yaml:"NUMBER_OF_COLUMNS"` + DataColumnSidecarSubnetCount *uint64 `yaml:"DATA_COLUMN_SIDECAR_SUBNET_COUNT"` + CustodyRequirement *uint64 `yaml:"CUSTODY_REQUIREMENT"` + // additional dora specific specs WhiskForkEpoch *uint64 } diff --git a/clients/consensus/rpc/beaconapi.go b/clients/consensus/rpc/beaconapi.go index 1be7a0d..a4ef43d 100644 --- a/clients/consensus/rpc/beaconapi.go +++ b/clients/consensus/rpc/beaconapi.go @@ -467,7 +467,16 @@ func (bc *BeaconClient) GetNodePeers(ctx context.Context) ([]*v1.Peer, error) { if err != nil { return nil, err } - return result.Data, nil + + // Temporary workaround to filter out peers that are not connected (https://github.com/grandinetech/grandine/issues/46) + filteredPeers := make([]*v1.Peer, 0) + for _, peer := range result.Data { + if peer.State == "connected" { + filteredPeers = append(filteredPeers, peer) + } + } + + return filteredPeers, nil } func (bc *BeaconClient) GetNodeIdentity(ctx context.Context) (*NodeIdentity, error) { diff --git a/go.mod b/go.mod index 637a07e..fbabc10 100644 --- a/go.mod +++ b/go.mod @@ -30,12 +30,34 @@ require ( github.com/timandy/routine v1.1.4 github.com/urfave/negroni v1.0.0 golang.org/x/crypto v0.28.0 - golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 golang.org/x/text v0.19.0 golang.org/x/time v0.7.0 gopkg.in/yaml.v3 v3.0.1 ) +require ( + github.com/ipfs/go-cid v0.4.1 // indirect + github.com/libp2p/go-buffer-pool v0.1.0 // indirect + github.com/libp2p/go-libp2p v0.36.5 // indirect + github.com/minio/highwayhash v1.0.2 // indirect + github.com/mr-tron/base58 v1.2.0 // indirect + github.com/multiformats/go-base32 v0.1.0 // indirect + github.com/multiformats/go-base36 v0.2.0 // indirect + github.com/multiformats/go-multiaddr v0.13.0 // indirect + github.com/multiformats/go-multibase v0.2.0 // indirect + github.com/multiformats/go-multicodec v0.9.0 // indirect + github.com/multiformats/go-multihash v0.2.3 // indirect + github.com/multiformats/go-varint v0.0.7 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/prysmaticlabs/fastssz v0.0.0-20240620202422-a981b8ef89d3 // indirect + github.com/prysmaticlabs/gohashtree v0.0.4-beta.0.20240624100937-73632381301b // indirect + github.com/prysmaticlabs/prysm/v5 v5.1.0 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e // indirect + lukechampine.com/blake3 v1.3.0 // indirect +) + require ( dario.cat/mergo v1.0.1 // indirect github.com/Masterminds/goutils v1.1.1 // indirect @@ -91,7 +113,7 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.19.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.54.0 // indirect + github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/r3labs/sse/v2 v2.10.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect diff --git a/go.sum b/go.sum index a3aedc4..f7f26e3 100644 --- a/go.sum +++ b/go.sum @@ -179,6 +179,8 @@ github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= +github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= +github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= @@ -269,6 +271,10 @@ github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= +github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= +github.com/libp2p/go-libp2p v0.36.5 h1:DoABsaHO0VXwH6pwCs2F6XKAXWYjFMO4HFBoVxTnF9g= +github.com/libp2p/go-libp2p v0.36.5/go.mod h1:CpszAtXxHYOcyvB7K8rSHgnNlh21eKjYbEfLoMerbEI= github.com/lightclient/go-ethereum v0.0.0-20240907155054-183e7b702a00 h1:6fd42xMvs6JF4vN0SBB7bI1ILR4wHl53dn89YNsdpWY= github.com/lightclient/go-ethereum v0.0.0-20240907155054-183e7b702a00/go.mod h1:QeW+MtTpRdBEm2pUFoonByee8zfHv7kGp0wK0odvU1I= github.com/mashingan/smapping v0.1.19 h1:SsEtuPn2UcM1croIupPtGLgWgpYRuS0rSQMvKD9g2BQ= @@ -290,6 +296,8 @@ github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY= github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg= +github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= +github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= @@ -303,6 +311,24 @@ github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= +github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= +github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= +github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= +github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= +github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= +github.com/multiformats/go-multiaddr v0.13.0 h1:BCBzs61E3AGHcYYTv8dqRH43ZfyrqM8RXVPT8t13tLQ= +github.com/multiformats/go-multiaddr v0.13.0/go.mod h1:sBXrNzucqkFJhvKOiwwLyqamGa/P5EIXNPLovyhQCII= +github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= +github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk= +github.com/multiformats/go-multicodec v0.9.0 h1:pb/dlPnzee/Sxv/j4PmkDRxCOi3hXTz3IbPKOXWJkmg= +github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k= +github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U= +github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= +github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= +github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= @@ -334,6 +360,8 @@ github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.54.0 h1:ZlZy0BgJhTwVZUn7dLOkwCZHUkrAqd3WYtcFCWnM1D8= github.com/prometheus/common v0.54.0/go.mod h1:/TQgMJP5CuVYveyT7n/0Ix8yLNNXy9yRSkhnLTHPDIQ= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/protolambda/bls12-381-util v0.1.0 h1:05DU2wJN7DTU7z28+Q+zejXkIsA/MF8JZQGhtBZZiWk= @@ -342,8 +370,14 @@ github.com/protolambda/zrnt v0.32.3 h1:b3mkBEjcmxtft115cBIQk+2qz1HEb2ExDdduVQqN4 github.com/protolambda/zrnt v0.32.3/go.mod h1:A0fezkp9Tt3GBLATSPIbuY4ywYESyAuc/FFmPKg8Lqs= github.com/protolambda/ztyp v0.2.2 h1:rVcL3vBu9W/aV646zF6caLS/dyn9BN8NYiuJzicLNyY= github.com/protolambda/ztyp v0.2.2/go.mod h1:9bYgKGqg3wJqT9ac1gI2hnVb0STQq7p/1lapqrqY1dU= +github.com/prysmaticlabs/fastssz v0.0.0-20240620202422-a981b8ef89d3 h1:0LZAwwHnsZFfXm4IK4rzFV4N5IVSKZKLmuBMA4kAlFk= +github.com/prysmaticlabs/fastssz v0.0.0-20240620202422-a981b8ef89d3/go.mod h1:h2OlIZD/M6wFvV3YMZbW16lFgh3Rsye00G44J2cwLyU= github.com/prysmaticlabs/go-bitfield v0.0.0-20240618144021-706c95b2dd15 h1:lC8kiphgdOBTcbTvo8MwkvpKjO0SlAgjv4xIK5FGJ94= github.com/prysmaticlabs/go-bitfield v0.0.0-20240618144021-706c95b2dd15/go.mod h1:8svFBIKKu31YriBG/pNizo9N0Jr9i5PQ+dFkxWg3x5k= +github.com/prysmaticlabs/gohashtree v0.0.4-beta.0.20240624100937-73632381301b h1:VK7thFOnhxAZ/5aolr5Os4beiubuD08WiuiHyRqgwks= +github.com/prysmaticlabs/gohashtree v0.0.4-beta.0.20240624100937-73632381301b/go.mod h1:HRuvtXLZ4WkaB1MItToVH2e8ZwKwZPY5/Rcby+CvvLY= +github.com/prysmaticlabs/prysm/v5 v5.1.0 h1:TY9A6tm0v7bI1z9YH+xkDh7XH7qm4ZK8sTeyckxbj4A= +github.com/prysmaticlabs/prysm/v5 v5.1.0/go.mod h1:SWb5kE/FhtQrLS2yt+IDj+leB7IhXrcOv6lhDnU1nBY= github.com/r3labs/sse/v2 v2.10.0 h1:hFEkLLFY4LDifoHdiCN/LlGBAdVJYsANaLqNYa1l/v0= github.com/r3labs/sse/v2 v2.10.0/go.mod h1:Igau6Whc+F17QUgML1fYe1VPZzTV6EMCnYktEmkNJ7I= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= @@ -376,6 +410,8 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= @@ -405,6 +441,8 @@ github.com/tdewolff/parse v2.3.4+incompatible h1:x05/cnGwIMf4ceLuDMBOdQ1qGniMoxp github.com/tdewolff/parse v2.3.4+incompatible/go.mod h1:8oBwCsVmUkgHO8M5iCzSIDtpzXOT0WXX9cWhz+bIzJQ= github.com/tdewolff/test v1.0.9 h1:SswqJCmeN4B+9gEAi/5uqT0qpi1y2/2O47V/1hhGZT0= github.com/tdewolff/test v1.0.9/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= +github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e h1:cR8/SYRgyQCt5cNCMniB/ZScMkhI9nk8U5C7SbISXjo= +github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e/go.mod h1:Tu4lItkATkonrYuvtVjG0/rhy15qrNGNTjPdaphtZ/8= github.com/timandy/routine v1.1.4 h1:L9eAli/ROJcW6LhmwZcusYQcdAqxAXGOQhEXLQSNWOA= github.com/timandy/routine v1.1.4/go.mod h1:siBcl8iIsGmhLCajRGRcy7Y7FVcicNXkr97JODdt9fc= github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU= @@ -460,6 +498,8 @@ golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= @@ -491,6 +531,7 @@ golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -596,6 +637,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= +lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ= modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y= diff --git a/handlers/clients_cl.go b/handlers/clients_cl.go index ca00776..6ddb45b 100644 --- a/handlers/clients_cl.go +++ b/handlers/clients_cl.go @@ -4,6 +4,7 @@ import ( "fmt" "net/http" "sort" + "strconv" "strings" "time" @@ -141,11 +142,21 @@ func buildCLClientsPageData() (*models.ClientsCLPageData, time.Duration) { Clients: []*models.ClientsCLPageDataClient{}, PeerMap: buildCLPeerMapData(), ShowSensitivePeerInfos: utils.Config.Frontend.ShowSensitivePeerInfos, + ShowPeerDASInfos: utils.Config.Frontend.ShowPeerDASInfos, + PeerDASInfos: &models.ClientCLPagePeerDAS{ + Warnings: models.ClientCLPageDataPeerDASWarnings{ + MissingENRsPeers: []string{}, + MissingCSCFromENRPeers: []string{}, + EmptyColumns: []uint64{}, + }, + }, + Nodes: make(map[string]*models.ClientCLNode), } chainState := services.GlobalBeaconService.GetChainState() var cacheTime time.Duration - if specs := chainState.GetSpecs(); specs != nil { + specs := chainState.GetSpecs() + if specs != nil { cacheTime = specs.SecondsPerSlot } else { cacheTime = 1 * time.Second @@ -163,6 +174,21 @@ func buildCLClientsPageData() (*models.ClientsCLPageData, time.Duration) { for _, client := range services.GlobalBeaconService.GetConsensusClients() { lastHeadSlot, lastHeadRoot := client.GetLastHead() + id := client.GetNodeIdentity() + if id == nil { + continue + } + + // Add client to global nodes map + if _, ok := pageData.Nodes[id.PeerID]; !ok { + pageData.Nodes[id.PeerID] = &models.ClientCLNode{ + PeerID: id.PeerID, + Alias: client.GetName(), + Type: "internal", + ENR: id.Enr, + } + } + peers := client.GetNodePeers() resPeers := []*models.ClientCLPageDataClientPeers{} @@ -175,17 +201,14 @@ func buildCLClientsPageData() (*models.ClientsCLPageData, time.Duration) { peerType = "internal" } - enrKeyValues := map[string]interface{}{} - var nodeID string - - if peer.Enr != "" { // Some clients might not announce the ENR of their peers - parsedEnr, err := utils.DecodeENR(peer.Enr) + peerENRKeyValues := map[string]interface{}{} + if peer.Enr != "" { + rec, err := utils.DecodeENR(peer.Enr) if err != nil { - logrus.WithFields(logrus.Fields{"client": client.GetName(), "peer_enr": peer.Enr}).Error("failed to decode peer enr. ", err) - parsedEnr = &enr.Record{} + logrus.WithFields(logrus.Fields{"node": client.GetName(), "peer": peer.PeerID, "enr": peer.Enr}).Error("failed to decode peer enr. ", err) + rec = &enr.Record{} } - enrKeyValues = utils.GetKeyValuesFromENR(parsedEnr) - nodeID = utils.GetNodeIDFromENR(parsedEnr) + peerENRKeyValues = utils.GetKeyValuesFromENR(rec) } resPeers = append(resPeers, &models.ClientCLPageDataClientPeers{ @@ -195,17 +218,48 @@ func buildCLClientsPageData() (*models.ClientsCLPageData, time.Duration) { Alias: peerAlias, Type: peerType, ENR: peer.Enr, - ENRKeyValues: enrKeyValues, - NodeID: nodeID, + ENRKeyValues: peerENRKeyValues, LastSeenP2PAddress: peer.LastSeenP2PAddress, }) + // Add peer to global nodes map + _, ok := pageData.Nodes[peer.PeerID] + if !ok { + pageData.Nodes[peer.PeerID] = &models.ClientCLNode{ + PeerID: peer.PeerID, + Alias: peerAlias, + Type: peerType, + ENR: peer.Enr, + } + } + + node := pageData.Nodes[peer.PeerID] + if node.ENR == "" && peer.Enr != "" { + node.ENR = peer.Enr + } else if node.ENR != "" && peer.Enr != "" { + // Need to compare `seq` field from ENRs and only store highest + nodeENR, errA := utils.DecodeENR(node.ENR) + if errA != nil { + logrus.WithFields(logrus.Fields{"node": node.Alias, "enr": node.ENR}).Error("failed to decode enr of a node ", errA) + } + peerENR, errB := utils.DecodeENR(peer.Enr) + if errB != nil { + logrus.WithFields(logrus.Fields{"node": node.Alias, "peer": peer.PeerID, "enr": peer.Enr}).Error("failed to decode enr of a peer ", errB) + } + if errA == nil && errB == nil && peerENR.Seq() > nodeENR.Seq() { + node.ENR = peer.Enr // peerENR has higher sequence number, so override. + } + } + + // Increase peer direction counter if peer.Direction == "inbound" { inPeerCount++ } else { outPeerCount++ } } + + // Sort peers by type and alias sort.Slice(resPeers, func(i, j int) bool { if resPeers[i].Type == resPeers[j].Type { return resPeers[i].Alias < resPeers[j].Alias @@ -213,21 +267,6 @@ func buildCLClientsPageData() (*models.ClientsCLPageData, time.Duration) { return resPeers[i].Type > resPeers[j].Type }) - id := client.GetNodeIdentity() - if id == nil { - continue - } - - rec, err := utils.DecodeENR(id.Enr) - if err != nil { - logrus.WithFields(logrus.Fields{"client": client.GetName(), "enr": id.Enr}).Error("failed to decode enr. ", err) - rec = &enr.Record{} - } - - enrkv := utils.GetKeyValuesFromENR(rec) - - nodeID := utils.GetNodeIDFromENR(rec) - resClient := &models.ClientsCLPageDataClient{ Index: int(client.GetIndex()) + 1, Name: client.GetName(), @@ -235,8 +274,6 @@ func buildCLClientsPageData() (*models.ClientsCLPageData, time.Duration) { Peers: resPeers, PeerID: id.PeerID, ENR: id.Enr, - ENRKeyValues: enrkv, - NodeID: nodeID, P2PAddresses: id.P2PAddresses, DisoveryAddresses: id.DiscoveryAddresses, AttestationSubnetSubs: id.Metadata.Attnets, @@ -258,5 +295,157 @@ func buildCLClientsPageData() (*models.ClientsCLPageData, time.Duration) { } pageData.ClientCount = uint64(len(pageData.Clients)) + // Add peer in/out infos to global nodes map + for _, edge := range pageData.PeerMap.ClientDataMapEdges { + pageData.Nodes[edge.From].PeersOut = append(pageData.Nodes[edge.From].PeersOut, edge.To) + pageData.Nodes[edge.To].PeersIn = append(pageData.Nodes[edge.To].PeersIn, edge.From) + } + + columnDistribution := make(map[uint64]map[string]bool) + resultColumnDistribution := make(map[uint64][]string) + + // Verify and parse PeerDAS spec config + if specs != nil { + if specs.NumberOfColumns != nil { + pageData.PeerDASInfos.NumberOfColumns = *specs.NumberOfColumns + } else { + pageData.PeerDASInfos.NumberOfColumns = 128 + logrus.Warnf("NUMBER_OF_COLUMNS is not defined in spec, defaulting to %d", pageData.PeerDASInfos.NumberOfColumns) + pageData.PeerDASInfos.Warnings.MissingSpecValues = true + pageData.PeerDASInfos.Warnings.HasWarnings = true + } + + if specs.DataColumnSidecarSubnetCount != nil { + pageData.PeerDASInfos.DataColumnSidecarSubnetCount = *specs.DataColumnSidecarSubnetCount + } else { + pageData.PeerDASInfos.DataColumnSidecarSubnetCount = 128 + logrus.Warnf("DATA_COLUMN_SIDECAR_SUBNET_COUNT is not defined in spec, defaulting to %d", pageData.PeerDASInfos.DataColumnSidecarSubnetCount) + pageData.PeerDASInfos.Warnings.MissingSpecValues = true + pageData.PeerDASInfos.Warnings.HasWarnings = true + } + + if specs.CustodyRequirement != nil { + pageData.PeerDASInfos.CustodyRequirement = *specs.CustodyRequirement + } else { + pageData.PeerDASInfos.CustodyRequirement = 4 + logrus.Warnf("CUSTODY_REQUIREMENT is not defined in spec, defaulting to %d", pageData.PeerDASInfos.CustodyRequirement) + pageData.PeerDASInfos.Warnings.MissingSpecValues = true + pageData.PeerDASInfos.Warnings.HasWarnings = true + } + } + + // Calculate additional fields for nodes: ENR key values, Node ID, Custody Columns, Custody Column Subnets + for _, v := range pageData.Nodes { + + // Calculate K:V pairs for ENR + if v.ENR != "" { + rec, err := utils.DecodeENR(v.ENR) + if err != nil { + logrus.WithFields(logrus.Fields{"node": v.Alias, "enr": v.ENR}).Error("failed to decode enr. ", err) + rec = &enr.Record{} + } + v.ENRKeyValues = utils.GetKeyValuesFromENR(rec) + } else { + pageData.PeerDASInfos.Warnings.MissingENRsPeers = append(pageData.PeerDASInfos.Warnings.MissingENRsPeers, v.PeerID) + pageData.PeerDASInfos.Warnings.HasWarnings = true + } + + // Calculate node ID + nodeID, err := utils.ConvertPeerIDStringToEnodeID(v.PeerID) + if err != nil { + logrus.WithFields(logrus.Fields{"node": v.Alias, "peer_id": v.PeerID}).Error("failed to convert peer id to enode id. ", err) + } + v.NodeID = nodeID.String() + + custodySubnetCount := pageData.PeerDASInfos.CustodyRequirement + + if cscHex, ok := v.ENRKeyValues["csc"]; ok { + val, err := strconv.ParseUint(cscHex.(string), 0, 64) + if err != nil { + logrus.WithFields(logrus.Fields{"node": v.Alias, "peer_id": v.PeerID, "csc": cscHex.(string)}).Error("failed to decode csc. ", err) + } else { + custodySubnetCount = val + } + } else { + pageData.PeerDASInfos.Warnings.MissingCSCFromENRPeers = append(pageData.PeerDASInfos.Warnings.MissingCSCFromENRPeers, v.PeerID) + pageData.PeerDASInfos.Warnings.HasWarnings = true + } + + // Calculate custody columns and subnets for peer DAS + resColumns, err := utils.CustodyColumnsSlice(nodeID, custodySubnetCount, pageData.PeerDASInfos.NumberOfColumns, pageData.PeerDASInfos.DataColumnSidecarSubnetCount) + if err != nil { + logrus.WithFields(logrus.Fields{"node": v.Alias, "node_id": nodeID}).Error("failed to get custody columns. ", err) + } + + resSubnets, err := utils.CustodyColumnSubnetsSlice(nodeID, custodySubnetCount, pageData.PeerDASInfos.DataColumnSidecarSubnetCount) + if err != nil { + logrus.WithFields(logrus.Fields{"client": v.Alias, "node_id": nodeID}).Error("failed to get custody column subnets. ", err) + } + + // Transform the custody columns to a map for easier access + for _, idx := range resColumns { + if _, ok := columnDistribution[idx]; !ok { + columnDistribution[idx] = make(map[string]bool) + } + columnDistribution[idx][v.PeerID] = true + } + + peerDASInfo := models.ClientCLPageDataPeerDAS{ + CustodyColumns: resColumns, + CustodyColumnSubnets: resSubnets, + CustodySubnetCount: custodySubnetCount, + IsSuperNode: uint64(len(resColumns)) == pageData.PeerDASInfos.NumberOfColumns, + } + v.PeerDAS = &peerDASInfo + } + + // Transform the column distribution to a slice + for k, v := range columnDistribution { + for key := range v { + if _, ok := resultColumnDistribution[k]; !ok { + resultColumnDistribution[k] = []string{} + } + resultColumnDistribution[k] = append(resultColumnDistribution[k], key) + } + + // Sort the peer IDs by type and alias + sort.Slice(resultColumnDistribution[k], func(i, j int) bool { + pA := resultColumnDistribution[k][i] + pB := resultColumnDistribution[k][j] + nodeA := pageData.Nodes[pA] + nodeB := pageData.Nodes[pB] + + // Compare supernodes + if nodeA.PeerDAS.IsSuperNode != nodeB.PeerDAS.IsSuperNode { + return nodeA.PeerDAS.IsSuperNode + } + // Compare node types + if nodeA.Type != nodeB.Type { + return nodeA.Type > nodeB.Type + } + + // If types are the same, compare CustodyColumns length + lenA := len(nodeA.PeerDAS.CustodyColumns) + lenB := len(nodeB.PeerDAS.CustodyColumns) + if lenA != lenB { + return lenA > lenB + } + + // If both types and CustodyColumns lengths are the same, compare aliases + return nodeA.Alias < nodeB.Alias + }) + } + + // Check for empty columns + for i := uint64(0); i < pageData.PeerDASInfos.NumberOfColumns; i++ { + if _, ok := resultColumnDistribution[i]; !ok { + pageData.PeerDASInfos.Warnings.EmptyColumns = append(pageData.PeerDASInfos.Warnings.EmptyColumns, i) + pageData.PeerDASInfos.Warnings.HasWarnings = true + } + } + + pageData.PeerDASInfos.TotalRows = int(pageData.PeerDASInfos.NumberOfColumns) / 32 + pageData.PeerDASInfos.ColumnDistribution = resultColumnDistribution + return pageData, cacheTime } diff --git a/static/css/clients.css b/static/css/clients.css index 6c98b7e..811102c 100644 --- a/static/css/clients.css +++ b/static/css/clients.css @@ -138,3 +138,59 @@ Client peers table margin-left: -50px; margin-top: -50px; } + +.dastablepeercount { + font-weight:300; + font-size: 0.6rem; + line-height: 0.5rem; + margin-bottom: 2px; +} + +.dastablenode { + cursor: pointer; + max-width: 25px; + margin: 0 auto; + border: 1px solid #cacaca; + -webkit-transition: opacity 0.3s ease; + -moz-transition: opacity 0.3s ease; + -o-transition: opacity 0.3s ease; + transition: opacity 0.3s ease; + width: 17px; + height: 17px; + + border-radius: 0; +} + +.dastablenode.internal { + border-color: #ffa500ab +} + +.dastablenode.external { + border-color: #075e4d +} + +.dastablenode.supernode { + background-color: rgb(0 246 255 / 20%); + border-radius: 0; +} + +.dastablenode.supernode.highlight { + border-width: 1px !important; +} + +.dastablenode.highlight { + opacity: 1 !important; + border: 1px solid yellow !important; +} + +.dastablenode.blur { + opacity: 0.2; +} + +.peerdetails-modal-peer{ + cursor: pointer; +} + +.peerdetails-modal-peer:hover{ + font-weight: 600; +} diff --git a/static/js/explorer.js b/static/js/explorer.js index ff9c757..83372c3 100644 --- a/static/js/explorer.js +++ b/static/js/explorer.js @@ -2,6 +2,7 @@ (function() { window.addEventListener('DOMContentLoaded', function() { initControls(); + modalFixes(); window.setInterval(updateTimers, 1000); initHeaderSearch(); }); @@ -13,14 +14,32 @@ tooltipDict: tooltipDict, }; + function modalFixes() { + // Fix bootstrap backdrop stacking when having multiple modals + $(document).on('show.bs.modal', '.modal', function() { + const offset = (10 * $('.modal:visible').length); + const zIndex = 2000 + offset; + $(this).css('z-index', zIndex); + setTimeout(() => $('.modal-backdrop').not('.modal-stack').css('z-index', zIndex - offset - 1).addClass('modal-stack')); + }); + // Fix bootstrap scrolling stacking when having multiple modals + $(document).on('hidden.bs.modal', '.modal', function(){ + $('.modal:visible').length && $(document.body).addClass('modal-open') + }); + } + function initControls() { - // init tooltips + // init tooltips: + // NOTE: `data-bs-toogle="tooltip"`` tooltips will also get cleaned up if their relevant element is removed from the DOM document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(initTooltip); cleanupTooltips(); + // NOTE: `data-toogle="tooltip"` tooltips will not get cleaned up if they are removed from the DOM + $('[data-toggle="tooltip"]').tooltip() // init clipboard buttons - document.querySelectorAll("[data-clipboard-text]").forEach(initCopyBtn); - document.querySelectorAll("[data-clipboard-target]").forEach(initCopyBtn); + var clipboard = new ClipboardJS('[data-clipboard-text], [data-clipboard-target]'); + clipboard.on("success", onClipboardSuccess); + clipboard.on("error", onClipboardError); } function initTooltip(el) { @@ -46,15 +65,6 @@ }); } - function initCopyBtn(el) { - if($(el).data("clipboard-init")) - return; - $(el).data("clipboard-init", true); - var clipboard = new ClipboardJS(el); - clipboard.on("success", onClipboardSuccess); - clipboard.on("error", onClipboardError); - } - function onClipboardSuccess(e) { var title = e.trigger.getAttribute("data-bs-original-title"); var tooltip = bootstrap.Tooltip.getInstance(e.trigger); @@ -81,7 +91,7 @@ var time = timerEl.getAttribute("data-timer"); var textEls = Array.prototype.filter.call(timerEl.querySelectorAll("*"), function(el) { return el.firstChild && el.firstChild.nodeType === 3 }); var textEl = textEls.length ? textEls[0] : timerEl; - + textEl.innerText = renderRecentTime(time); }); } @@ -256,19 +266,19 @@ }, } ) - + searchEl.on("input", function (input) { $(".tt-suggestion").first().addClass("tt-cursor") }) - + jQuery(".tt-menu").on("mouseenter", function () { $(".tt-suggestion").first().removeClass("tt-cursor") }) - + jQuery(".tt-menu").on("mouseleave", function () { $(".tt-suggestion").first().addClass("tt-cursor") }) - + searchEl.on("typeahead:select", function (ev, sug) { if (sug.root !== undefined) { if (sug.orphaned) { @@ -294,5 +304,5 @@ }) } - + })() diff --git a/templates/clients/clients_cl.html b/templates/clients/clients_cl.html index c80acd4..ba999c3 100644 --- a/templates/clients/clients_cl.html +++ b/templates/clients/clients_cl.html @@ -1,4 +1,28 @@ {{ define "page" }} + +{{ $root := . }} + + + +

Consensus clients

@@ -13,7 +37,7 @@

Consensus clients<

-

@@ -25,12 +49,12 @@

-
+
- - - - + + + +
@@ -38,6 +62,179 @@

+ + {{ if $root.ShowPeerDASInfos }} +
+
+
+

+ +

+
+
+
+ {{ if $root.PeerDASInfos.Warnings.HasWarnings }} + + {{ end}} +

+ Spec configuration: + NUMBER_OF_COLUMNS={{ $root.PeerDASInfos.NumberOfColumns}}, + DATA_COLUMN_SIDECAR_SUBNET_COUNT={{ $root.PeerDASInfos.DataColumnSidecarSubnetCount}} + CUSTODY_REQUIREMENT={{ $root.PeerDASInfos.CustodyRequirement}} + Node count: {{ len $root.Nodes }} +

+ + +
+
+ + +
+
+ + +
+
+ + +
+ + {{ range $k, $p := $root.Nodes }} + +

+
+ + + + + {{ range $i := until $root.PeerDASInfos.TotalRows }} +
+ + + + {{ range $j := until 32 }} + {{ $idx := add ($j) (int (mul (float64 $i) (float64 32))) }} + {{ $peersPerColumn := index $root.PeerDASInfos.ColumnDistribution (int64 $idx) }} + {{ $peerCount := len $peersPerColumn }} + + {{ end }} + + + + + {{ range $j := until 32 }} + {{ $idx := add ($j) (int (mul (float64 $i) (float64 32))) }} + {{ $peersPerColumn := index $root.PeerDASInfos.ColumnDistribution (int64 $idx) }} + {{ $peerCount := len $peersPerColumn }} + + {{ end }} + +
+ {{ $idx }} +
({{ $peerCount }})
+
+
+ {{ with $peers := $peersPerColumn }} + {{ range $i, $p := $peers}} + {{ $peerData := index $root.Nodes $p }} + + + {{ end }} + {{ end }} +
+
+
+ {{ end }} +
+
+
+
+
+ {{ end }} +
@@ -61,7 +258,6 @@

- {{ $root := . }} {{ range $i, $client := .Clients }} {{ $client.Index }} @@ -78,39 +274,39 @@

- + {{ $client.PeersInboundCounter }} - + {{ $client.PeersOutboundCounter}} - + ({{ len $client.Peers }}) {{ formatAddCommas $client.HeadSlot }} 0x{{ printf "%x" $client.HeadRoot }} - + {{ if eq $client.Status "online" }} Ready {{ else if eq $client.Status "synchronizing" }} - Synchronizing + Synchronizing {{ else if eq $client.Status "optimistic" }} - Optimistic + Optimistic {{ else if eq $client.Status "offline" }} - Disconnected + Disconnected {{ else }} {{ $client.Status }} {{ end }} {{ $client.Version }} - + @@ -123,112 +319,180 @@

Name {{ $client.Name }} - + Peer ID {{ $client.PeerID }} - + - {{ if $root.ShowSensitivePeerInfos }} Node ID - {{ $client.NodeID }} - + {{ (index $root.Nodes $client.PeerID).NodeID }} + + {{ if $root.ShowSensitivePeerInfos }} ENR
{{ $client.ENR }} - +
{{ end }} - {{ if $root.ShowSensitivePeerInfos }} + {{ if $root.ShowSensitivePeerInfos }}
ENR fields
- - - {{ range $k, $v := $client.ENRKeyValues }} - - - - - {{ end }} - -
{{ $k }} - {{ $v }} - -
-

+ + + {{ range $k, $v := (index $root.Nodes $client.PeerID).ENRKeyValues }} + + + + + {{ end }} + +
{{ $k }} + {{ $v }} + +
+ + {{ if $root.ShowPeerDASInfos }} +
Peer DAS
+ + + + + + + + + + + +
Custody columns +
+ {{ (index $root.Nodes $client.PeerID).PeerDAS.CustodyColumns }} + +
+
Custody subnets +
+ {{ (index $root.Nodes $client.PeerID).PeerDAS.CustodyColumnSubnets }} + +
+
{{ end }} + {{ end }} +
Peers
{{ range $j, $peer := $client.Peers }}
{{ if eq $peer.Direction "inbound" }} - + {{ else }} - + {{ end}} {{ $peer.ID }} - + {{ if eq $peer.Type "internal" }} {{ $peer.Alias }} {{ end }}
{{ if $root.ShowSensitivePeerInfos }}
+ {{ $peerENR := (index $root.Nodes $peer.ID).ENR}} + {{ $reportedPeerENR := $peer.ENR}} + {{ if ne $peerENR $reportedPeerENR }} + + {{ end }} + + + + - {{ if ne $peer.ENR "" }} - - - - - {{ range $k, $v := $peer.ENRKeyValues }} + {{ if ne $peerENR "" }} + {{ range $k, $v := (index $root.Nodes $peer.ID).ENRKeyValues }} {{ end }} {{ end }}
Node ID + {{ (index $root.Nodes $peer.ID).NodeID }} + +
P2P Addr {{ $peer.LastSeenP2PAddress }} - +
ENR
- {{ if eq $peer.ENR "" }}Unknown{{ else }}{{ $peer.ENR }}{{ end }} - + {{ if eq $peerENR "" }}Unknown{{ else }}{{ $peerENR }}{{ end }} +
Node ID - {{ $peer.NodeID }} - -
{{ $k }} {{ $v }} - +
+ {{ if $root.ShowPeerDASInfos }} +
Peer DAS
+ + + + + + + + + + + +
Custody columns +
+ {{ (index $root.Nodes $peer.ID).PeerDAS.CustodyColumns }} + +
+
Custody subnets +
+ {{ (index $root.Nodes $peer.ID).PeerDAS.CustodyColumnSubnets }} + +
+
+ {{ end }}
{{ end}} {{ end }} @@ -271,6 +535,240 @@

var container = document.getElementById("nodemap"); var data = {{ .PeerMap }}; var cy = $_network.create(container, data); + + var nodes = {{ .Nodes }}; + + var showPeerDetailsModal = function(peerID){ + jdenticon.update("#peerDetailsModalTitleImage", peerID); + $('#peerDetailsModalTitle').html(`${nodes[peerID].alias}`); + + let peerDetailsTemplate = ` + + + + + + + + + + + + + + `; + + if (nodes[peerID].enr_kv) { + peerDetailsTemplate += ` + + + + ${Object.entries(nodes[peerID].enr_kv).map(([key, value]) => ` + + + + + `).join('')} + `; + } + + peerDetailsTemplate += ` + + + + + + + + + + + + + + + + + + + + + + + `; + + if (nodes[peerID].peers_in != null){ + nodes[peerID].peers_in.forEach(element => { + peerDetailsTemplate += ` + + + `; + }); + } + + if (nodes[peerID].peers_out != null){ + nodes[peerID].peers_out.forEach(element => { + peerDetailsTemplate += ` + + + `; + }); + } + peerDetailsTemplate += `
Peer ID${peerID}
Node ID${nodes[peerID].node_id}
ENR${nodes[peerID].enr?nodes[peerID].enr:"Unknown"}
ENR Fields
${key}${value}
Peer DAS
Supernode${nodes[peerID].peer_das.is_super_node}
CSC${nodes[peerID].peer_das.custody_subnet_count}
Columns${nodes[peerID].peer_das.custody_columns}
Subnets${nodes[peerID].peer_das.custody_column_subnets}
Peers
+ + + + ${element} ${nodes[element].alias!=element?""+nodes[element].alias+"":""} + +
+ + + + ${element} ${nodes[element].alias!=element?""+nodes[element].alias+"":""} + +
`; + + //peerDetailsTemplate += `
${JSON.stringify(nodes[peerID], null, 2)}
`; + $('#peerDetailsModalBody').html(peerDetailsTemplate) + jdenticon.update(".peer-details-jdenticon",null) + $('#peerDetailsModal').modal('show'); + } + + var lastClickedNode = null; + + // PeerDASTable Filter + function filterDASTablePeers() { + // Get the status of each checkbox + var showExternal = $('#showExternalPeersDAS').is(':checked'); + var showInternal = $('#showInternalPeersDAS').is(':checked'); + var showSupernode = $('#showSupernodePeersDAS').is(':checked'); + + console.log(showExternal, showInternal, showSupernode); + + // For each node, show or hide based on the filter status + $('.dastablenode').each(function() { + var isExternal = $(this).hasClass('external'); + var isInternal = $(this).hasClass('internal'); + var isSupernode = $(this).hasClass('supernode'); + + + console.log(isExternal, isInternal, isSupernode); + + // Show supernodes if the checkbox is checked + if (isSupernode && showSupernode) { + $(this).show(); + return; + } else if (isSupernode && !showSupernode) { + $(this).hide(); + return; + } + + // Show the node if it matches any of the active types + if ((showExternal && isExternal) || + (showInternal && isInternal)) { + $(this).show(); + } else { + $(this).hide(); + } + + + + + }); + + } + + // PeerDASTable Hover + let hoverTimeout; + $('.dastablenode').hover( + function() { + hoverTimeout = setTimeout(() => { + const searchText = $('#searchDASTablePeers').val().trim().toLowerCase(); + + if (!searchText && lastClickedNode == null) { + const $target = $(this); + const selector = $target.attr('class').split(/\s+/).map(cls => cls!=""?'.' + cls:"").join(''); + $(selector).addClass('highlight'); + $('.dastablenode').addClass('blur'); + } + }, 500); //500ms delay before highlighting + }, + function() { + clearTimeout(hoverTimeout); // Clear the timeout if the mouse leaves before 2 seconds + const searchText = $('#searchDASTablePeers').val().trim().toLowerCase(); + + if (!searchText && lastClickedNode == null) { + const $target = $(this); + const selector = $target.attr('class').split(/\s+/).map(cls => cls!=""?'.' + cls:"").join(''); + $(selector).removeClass('highlight'); + $('.dastablenode').removeClass('blur'); + } + } + ); + + // PeerDASTable Click + $('.dastablenode').on('click', function() { + const $target = $(this); + const selector = $target.attr('class').split(/\s+/).map(cls => '.' + cls).join(''); + + const peerID = $target.data('peerid').toString(); + + + showPeerDetailsModal(peerID); + + // Remove highlight/blur from the last clicked node + if (lastClickedNode != null) { + $(lastClickedNode).removeClass('highlight'); + $('.dastablenode').removeClass('blur'); + } + + // Highlight clicked node and blur others + $(selector).addClass('highlight'); + $('.dastablenode').not(selector).addClass('blur'); + + // Store the clicked node + lastClickedNode = selector; + + // Add node to searchtext + $('#searchDASTablePeers').val(nodes[peerID].alias).trigger("input"); + + }); + + // Event listener to remove highlight when clicking outside + $(document).on('click', function(event) { + const searchText = $('#searchDASTablePeers').val().trim().toLowerCase(); + + if (!searchText && !$(event.target).closest('.dastablenode').length) { + // Remove highlight and blur when clicking outside a .dastablenode element + $('.dastablenode').removeClass('highlight blur'); + lastClickedNode = null; + } + }); + + // PeerDASTable search + $('#searchDASTablePeers').on('input', function() { + const searchText = $(this).val().trim().toLowerCase(); + + if (searchText) { + $('.dastablenode').each(function() { + const peerId = $(this).data('peerid').toString().toLowerCase(); + const alias = $(this).data('alias').toString().toLowerCase(); + if (peerId.includes(searchText) || alias.includes(searchText)) { + $(this).addClass('highlight').removeClass('blur'); // Add 'highlight' if matches, remove 'blur' + } else { + $(this).addClass('blur').removeClass('highlight'); // Add 'blur' if doesn't match, remove 'highlight' + } + }); + } else { + $('.dastablenode').removeClass('blur highlight'); // Remove both classes if search text is cleared + } + }); + + // Clear search input + $('#clearSearchButton').click(function(){ + $('#searchDASTablePeers').val(''); // Clear the input + }); + {{ end }} {{ define "css" }} diff --git a/templates/clients/clients_el.html b/templates/clients/clients_el.html index a6b9a4a..7efa223 100644 --- a/templates/clients/clients_el.html +++ b/templates/clients/clients_el.html @@ -25,7 +25,7 @@

-
+
@@ -278,6 +278,7 @@

+