diff --git a/clients/consensus/chainspec.go b/clients/consensus/chainspec.go index e5d39142..43311d65 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 1be7a0d2..a4ef43d0 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 637a07ed..fbabc103 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 a3aedc4f..f7f26e3c 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 ca007760..6ddb45b9 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 6c98b7ee..811102c5 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 ff9c7572..83372c35 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 c80acd4e..ba999c37 100644 --- a/templates/clients/clients_cl.html +++ b/templates/clients/clients_cl.html @@ -1,4 +1,28 @@ {{ define "page" }} + +{{ $root := . }} + + +
+
+ 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 }}
+
+ {{ $idx }}
+ ({{ $peerCount }})
+ |
+ {{ end }}
+
---|
+
+ {{ with $peers := $peersPerColumn }}
+ {{ range $i, $p := $peers}}
+ {{ $peerData := index $root.Nodes $p }}
+
+ {{ end }}
+ {{ end }}
+
+ |
+ {{ end }}
+
{{ $client.Name }}
-
+
{{ $client.PeerID }}
-
+
{{ $client.NodeID }}
-
+ {{ (index $root.Nodes $client.PeerID).NodeID }}
+
{{ $client.ENR }}
-
+
{{ $k }} | -
- {{ $v }}
-
- |
-
{{ $k }} | +
+ {{ $v }}
+
+ |
+
Custody columns | +
+
+
+ {{ (index $root.Nodes $client.PeerID).PeerDAS.CustodyColumns }}
+
+ |
+
Custody subnets | +
+
+
+ {{ (index $root.Nodes $client.PeerID).PeerDAS.CustodyColumnSubnets }}
+
+ |
+
{{ $peer.ID }}
-
+
{{ if eq $peer.Type "internal" }}
{{ $peer.Alias }}
{{ end }}
The ENR information was obtained from another node.
+ The node{{ $client.Name }}
did not have the most up-to-date ENR for this peer.
+ {{ if ne $reportedPeerENR "" }}
+ Some other node reported a higher sequence number than this one.
+ seq={{ index $peer.ENRKeyValues "seq" }}
vs seq={{ index (index $root.Nodes $peer.ID).ENRKeyValues "seq" }}
+
ENR: {{ $reportedPeerENR }}
Decoded ENR: {{ toJson $peer.ENRKeyValues }}
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 }}
-
+
|
Custody columns | +
+
+
+ {{ (index $root.Nodes $peer.ID).PeerDAS.CustodyColumns }}
+
+ |
+
Custody subnets | +
+
+
+ {{ (index $root.Nodes $peer.ID).PeerDAS.CustodyColumnSubnets }}
+
+ |
+
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+"":""}
+
+ |
+ `;
+ });
+ }
+
+ if (nodes[peerID].peers_out != null){
+ nodes[peerID].peers_out.forEach(element => {
+ peerDetailsTemplate += `
+ |
+
+
+
+ ${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 a6b9a4a0..7efa2232 100644
--- a/templates/clients/clients_el.html
+++ b/templates/clients/clients_el.html
@@ -25,7 +25,7 @@