From b6ad68dbe34d32cf91d16f454b3d53e3e006658e Mon Sep 17 00:00:00 2001 From: Alexey Kiselev Date: Tue, 29 Sep 2026 17:51:03 +0400 Subject: [PATCH 1/9] WIP: Updated Go to 1.27. --- connection_manager.go | 1 - go.mod | 71 +++++++------ go.sum | 231 ++++++++++++++++++------------------------ peers/peer.go | 2 +- 4 files changed, 134 insertions(+), 171 deletions(-) diff --git a/connection_manager.go b/connection_manager.go index fdca406..64f7ee6 100644 --- a/connection_manager.go +++ b/connection_manager.go @@ -43,7 +43,6 @@ func NewConnectionManager( proto.ContentIDScore: false, proto.ContentIDTransaction: true, proto.ContentIDInvMicroblock: false, - proto.ContentIDCheckpoint: true, proto.ContentIDMicroblockRequest: true, proto.ContentIDMicroblock: true, proto.ContentIDPBBlock: false, diff --git a/go.mod b/go.mod index 46d1d47..3a8d362 100644 --- a/go.mod +++ b/go.mod @@ -1,57 +1,56 @@ module github.com/alexeykiselev/waves-fork-detector -go 1.22.1 +go 1.27.0 require ( - github.com/fxamacker/cbor/v2 v2.6.0 - github.com/go-chi/chi/v5 v5.0.12 - github.com/qmuntal/stateless v1.6.9 + github.com/fxamacker/cbor/v2 v2.9.4 + github.com/go-chi/chi/v5 v5.3.2 + github.com/qmuntal/stateless v1.8.0 github.com/rhansen/go-kairos v0.0.0-20230622235007-87f08a46d19b - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.12.1 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 - github.com/wavesplatform/gowaves v0.10.7-0.20240517080008-6dac75167262 - go.uber.org/zap v1.27.0 - golang.org/x/sync v0.7.0 + github.com/wavesplatform/gowaves v0.11.1 + go.uber.org/zap v1.28.0 + golang.org/x/sync v0.23.0 ) require ( - filippo.io/edwards25519 v1.1.0 // indirect - github.com/bits-and-blooms/bitset v1.8.0 // indirect + filippo.io/edwards25519 v1.2.0 // indirect + github.com/bits-and-blooms/bitset v1.24.0 // indirect github.com/blang/semver/v4 v4.0.0 // indirect - github.com/btcsuite/btcd/btcec/v2 v2.3.3 // indirect - github.com/consensys/bavard v0.1.13 // indirect - github.com/consensys/gnark v0.10.0 // indirect - github.com/consensys/gnark-crypto v0.12.2-0.20240215234832-d72fcb379d3e // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.6 // indirect + github.com/ccoveille/go-safecast/v2 v2.0.0 // indirect + github.com/cloudflare/circl v1.6.3 // indirect + github.com/consensys/gnark v0.14.0 // indirect + github.com/consensys/gnark-crypto v0.19.2 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect - github.com/golang/snappy v0.0.4 // indirect + github.com/dpotapov/slogpfx v0.0.0-20230917063348-41a73c95c536 // indirect + github.com/golang/snappy v1.0.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b // indirect - github.com/ingonyama-zk/icicle v0.0.0-20230928131117-97f0079e5c71 // indirect - github.com/ingonyama-zk/iciclegnark v0.1.0 // indirect + github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 // indirect + github.com/ingonyama-zk/icicle-gnark/v3 v3.2.2 // indirect github.com/jinzhu/copier v0.4.0 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect - github.com/mmcloughlin/addchain v0.4.0 // indirect + github.com/lmittmann/tint v1.1.3 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mr-tron/base58 v1.2.0 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/rs/zerolog v1.30.0 // indirect - github.com/stretchr/objx v0.5.2 // indirect + github.com/planetscale/vtprotobuf v0.6.1-0.20250313105119-ba97887b0a25 // indirect + github.com/ronanh/intcomp v1.1.1 // indirect + github.com/rs/zerolog v1.34.0 // indirect + github.com/stretchr/objx v0.5.3 // indirect github.com/umbracle/fastrlp v0.1.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/x448/float16 v0.8.4 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.23.0 // indirect - golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect - golang.org/x/net v0.24.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect - google.golang.org/grpc v1.64.0 // indirect - google.golang.org/protobuf v1.34.1 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect - moul.io/zapfilter v1.7.0 // indirect - rsc.io/tmplfunc v0.0.3 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect + golang.org/x/crypto v0.49.0 // indirect + golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect + golang.org/x/net v0.51.0 // indirect + golang.org/x/sys v0.42.0 // indirect + golang.org/x/text v0.35.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect + google.golang.org/grpc v1.79.3 // indirect + google.golang.org/protobuf v1.36.11 // indirect ) diff --git a/go.sum b/go.sum index 4e4ac41..b34d423 100644 --- a/go.sum +++ b/go.sum @@ -1,35 +1,43 @@ -filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= -filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/bits-and-blooms/bitset v1.8.0 h1:FD+XqgOZDUxxZ8hzoBFuV9+cGWY9CslN6d5MS5JVb4c= -github.com/bits-and-blooms/bitset v1.8.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo= +filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= +github.com/bits-and-blooms/bitset v1.24.0 h1:H4x4TuulnokZKvHLfzVRTHJfFfnHEeSYJizujEZvmAM= +github.com/bits-and-blooms/bitset v1.24.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= -github.com/btcsuite/btcd/btcec/v2 v2.3.3 h1:6+iXlDKE8RMtKsvK0gshlXIuPbyWM/h84Ensb7o3sC0= -github.com/btcsuite/btcd/btcec/v2 v2.3.3/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/btcec/v2 v2.3.6 h1:IzlsEr9olcSRKB/n7c4351F3xHKxS2lma+1UFGCYd4E= +github.com/btcsuite/btcd/btcec/v2 v2.3.6/go.mod h1:m22FrOAiuxl/tht9wIqAoGHcbnCCaPWyauO8y2LGGtQ= github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= -github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= -github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= -github.com/consensys/gnark v0.10.0 h1:yhi6ThoeFP7WrH8zQDaO56WVXe9iJEBSkfrZ9PZxabw= -github.com/consensys/gnark v0.10.0/go.mod h1:VJU5JrrhZorbfDH+EUjcuFWr2c5z19tHPh8D6KVQksU= -github.com/consensys/gnark-crypto v0.12.2-0.20240215234832-d72fcb379d3e h1:MKdOuCiy2DAX1tMp2YsmtNDaqdigpY6B5cZQDJ9BvEo= -github.com/consensys/gnark-crypto v0.12.2-0.20240215234832-d72fcb379d3e/go.mod h1:wKqwsieaKPThcFkHe0d0zMsbHEUWFmZcG7KBCse210o= +github.com/ccoveille/go-safecast/v2 v2.0.0 h1:+5eyITXAUj3wMjad6cRVJKGnC7vDS55zk0INzJagub0= +github.com/ccoveille/go-safecast/v2 v2.0.0/go.mod h1:JIYA4CAR33blIDuE6fSwCp2sz1oOBahXnvmdBhOAABs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8= +github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= +github.com/consensys/gnark v0.14.0 h1:RG+8WxRanFSFBSlmCDRJnYMYYKpH3Ncs5SMzg24B5HQ= +github.com/consensys/gnark v0.14.0/go.mod h1:1IBpDPB/Rdyh55bQRR4b0z1WvfHQN1e0020jCvKP2Gk= +github.com/consensys/gnark-crypto v0.19.2 h1:qrEAIXq3T4egxqiliFFoNrepkIWVEeIYwt3UL0fvS80= +github.com/consensys/gnark-crypto v0.19.2/go.mod h1:rT23F0XSZqE0mUA0+pRtnL56IbPxs6gp4CeRsBk4XS0= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= +github.com/dpotapov/slogpfx v0.0.0-20230917063348-41a73c95c536 h1:3ZUyGIhpbUJVL3nwGRJO/DH1GRNb3qhKOteP1tMwFrA= +github.com/dpotapov/slogpfx v0.0.0-20230917063348-41a73c95c536/go.mod h1:L9xGyDDA8E/83ucQSIKU/ZU3YfS3BzhyynT0ykxJGCk= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA= -github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= -github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s= -github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/fxamacker/cbor/v2 v2.9.4 h1:xwjVlxEMR3S605oUlgBjKLTTeGFciYPGYCtF/35LKGo= +github.com/fxamacker/cbor/v2 v2.9.4/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/go-chi/chi/v5 v5.3.2 h1:5YQkICvTCSZ25hoRsyJazN0scjzKGiu4VAUc7H1o1nY= +github.com/go-chi/chi/v5 v5.3.2/go.mod h1:R+tYY2hNuVUUjxoPtqUdgBqevM9s9njzkTLutVsOCto= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= @@ -38,44 +46,38 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= +github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b h1:h9U78+dx9a4BKdQkBBos92HalKpaGKHrp+3Uo6yTodo= -github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= -github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= +github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 h1:EEHtgt9IwisQ2AZ4pIsMjahcegHh6rmhqxzIRQIyepY= +github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/ingonyama-zk/icicle v0.0.0-20230928131117-97f0079e5c71 h1:YxI1RTPzpFJ3MBmxPl3Bo0F7ume7CmQEC1M9jL6CT94= -github.com/ingonyama-zk/icicle v0.0.0-20230928131117-97f0079e5c71/go.mod h1:kAK8/EoN7fUEmakzgZIYdWy1a2rBnpCaZLqSHwZWxEk= -github.com/ingonyama-zk/iciclegnark v0.1.0 h1:88MkEghzjQBMjrYRJFxZ9oR9CTIpB8NG2zLeCJSvXKQ= -github.com/ingonyama-zk/iciclegnark v0.1.0/go.mod h1:wz6+IpyHKs6UhMMoQpNqz1VY+ddfKqC/gRwR/64W6WU= +github.com/ingonyama-zk/icicle-gnark/v3 v3.2.2 h1:B+aWVgAx+GlFLhtYjIaF0uGjU3rzpl99Wf9wZWt+Mq8= +github.com/ingonyama-zk/icicle-gnark/v3 v3.2.2/go.mod h1:CH/cwcr21pPWH+9GtK/PFaa4OGTv4CtfkCKro6GpbRE= github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= -github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4= +github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c= +github.com/lmittmann/tint v1.1.3 h1:Hv4EaHWXQr+GTFnOU4VKf8UvAtZgn0VuKT+G0wFlO3I= +github.com/lmittmann/tint v1.1.3/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -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/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 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/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= @@ -87,84 +89,70 @@ github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9k github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7/go.mod h1:zO8QMzTeZd5cpnIkz/Gn6iK0jDfGicM1nynOkkPIl28= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/qmuntal/stateless v1.6.9 h1:o2QDpwq8bF6n1DN8rKe8c4HZoVaUoF0rmQgihL/dMwA= -github.com/qmuntal/stateless v1.6.9/go.mod h1:n1HjRBM/cq4uCr3rfUjaMkgeGcd+ykAZwkjLje6jGBM= +github.com/planetscale/vtprotobuf v0.6.1-0.20250313105119-ba97887b0a25 h1:S1hI5JiKP7883xBzZAr1ydcxrKNSVNm7+3+JwjxZEsg= +github.com/planetscale/vtprotobuf v0.6.1-0.20250313105119-ba97887b0a25/go.mod h1:ZQntvDG8TkPgljxtA0R9frDoND4QORU1VXz015N5Ks4= +github.com/qmuntal/stateless v1.8.0 h1:9+Eg/7bWLKxUxs/vysNYAelFAh85kTyueC3ee6v8im8= +github.com/qmuntal/stateless v1.8.0/go.mod h1:KWa8KVzIBD/ZS0EdzL5oU79sGq7fKwH9WEFijTC5AWw= github.com/rhansen/go-kairos v0.0.0-20230622235007-87f08a46d19b h1:FRUYsvFJXa6A7kjDDnnzqGfwN44LfGfqvWjFCfPWdBY= github.com/rhansen/go-kairos v0.0.0-20230622235007-87f08a46d19b/go.mod h1:zpwvrKQ1hFbky5wnYOM1HoXwt9mmLqO3yx8JYgHlVfs= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c= -github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/ronanh/intcomp v1.1.1 h1:+1bGV/wEBiHI0FvzS7RHgzqOpfbBJzLIxkqMJ9e6yxY= +github.com/ronanh/intcomp v1.1.1/go.mod h1:7FOLy3P3Zj3er/kVrU/pl+Ql7JFZj7bwliMGketo0IU= +github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= +github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= +github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= +github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= +github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= -github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8= github.com/umbracle/fastrlp v0.1.0 h1:V0W3f6ZKWqbu1KggdhnRWOi+t7+PfL3VyAffJqayI5s= github.com/umbracle/fastrlp v0.1.0/go.mod h1:5RHgqiFjd4vLJESMWagP/E7su+5Gzk0iqqmrotR8WdA= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/wavesplatform/gowaves v0.10.7-0.20240517080008-6dac75167262 h1:xV4QYWCEfG14VV2UXuR1ffvbYb1zBUODlVPFNYQKYj0= -github.com/wavesplatform/gowaves v0.10.7-0.20240517080008-6dac75167262/go.mod h1:i95+8qFNshM9aX+q0JvFzcrw0JYZxeudt5CQaT0dy8Y= +github.com/wavesplatform/gowaves v0.11.1 h1:SdPl+sDKM0A4bxAwb5ycRE/fOUx77L5LBCCirBjom2g= +github.com/wavesplatform/gowaves v0.11.1/go.mod h1:9AKPZGLp9Wt3HzdkzbTpcIE8K8u32muOtvaeyxQieOw= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.8.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.20.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= +go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ= -golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= +golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= +golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0= +golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 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-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= +golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.23.0 h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk= +golang.org/x/sync v0.23.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -174,62 +162,39 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= +golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= -google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= -google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= +google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE= +google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -moul.io/zapfilter v1.7.0 h1:7aFrG4N72bDH9a2BtYUuUaDS981Dxu3qybWfeqaeBDU= -moul.io/zapfilter v1.7.0/go.mod h1:M+N2s+qZiA+bzRoyKMVRxyuERijS2ovi2pnMyiOGMvc= -rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= -rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= diff --git a/peers/peer.go b/peers/peer.go index 20b5763..9790e0a 100644 --- a/peers/peer.go +++ b/peers/peer.go @@ -72,7 +72,7 @@ func (p *Peer) RequestBlockIDs(ids []proto.BlockID) { } else { zap.S().Debugf("[%s] Requesting blocks IDs for IDs range [%s...%s]", p.p.ID().String(), ids[0].ShortString(), ids[len(ids)-1].ShortString()) - p.p.SendMessage(&proto.GetBlockIdsMessage{Blocks: ids}) + p.p.SendMessage(&proto.GetBlockIDsMessage{Blocks: ids}) } } From cf23189caf2944cd4f9546a20eaf5ed3f4314520 Mon Sep 17 00:00:00 2001 From: Alexey Kiselev Date: Wed, 30 Sep 2026 14:17:43 +0400 Subject: [PATCH 2/9] Zap logger replaced with slog. Log messages updated. GolangCI-lint configuration file updated. Dependencies updated. --- .golangci-strict.yml | 507 +++++++++++---------------- README.md | 5 +- api/api.go | 40 ++- chains/linkage.go | 24 +- chains/linkage_internal_test.go | 5 +- chains/storage_internal_test.go | 4 +- connection_manager.go | 19 +- distributor.go | 91 ++--- forkdetector.go | 83 +++-- go.mod | 49 ++- go.sum | 145 +++----- listener.go | 32 +- loading/loader.go | 52 +-- loading/peer_loader.go | 23 +- loading/peer_loader_internal_test.go | 11 +- loading/queue_internal_test.go | 8 +- parameters.go | 49 ++- peers/peer.go | 21 +- peers/registry.go | 33 +- respawn.go | 32 +- 20 files changed, 586 insertions(+), 647 deletions(-) diff --git a/.golangci-strict.yml b/.golangci-strict.yml index d7dad9b..a977443 100644 --- a/.golangci-strict.yml +++ b/.golangci-strict.yml @@ -1,315 +1,200 @@ -# This code is licensed under the terms of the MIT license. - -## Golden config for golangci-lint v1.53.3 -# -# This is the best config for golangci-lint based on my experience and opinion. -# It is very strict, but not extremely strict. -# Feel free to adopt and change it for your needs. - -run: - # Timeout for analysis, e.g. 30s, 5m. - # Default: 1m - timeout: 10m - - -# This file contains only configs which differ from defaults. -# All possible options can be found here https://github.com/golangci/golangci-lint/blob/master/.golangci.reference.yml -linters-settings: - cyclop: - # The maximal code complexity to report. - # Default: 10 - max-complexity: 30 - # The maximal average package complexity. - # If it's higher than 0.0 (float) the check is enabled - # Default: 0.0 - package-average: 10.0 - - errcheck: - # Report about not checking of errors in type assertions: `a := b.(MyStruct)`. - # Such cases aren't reported by default. - # Default: false - check-type-assertions: true - - exhaustive: - # Program elements to check for exhaustiveness. - # Default: [ switch ] - check: - - switch - - map - - exhaustruct: - # List of regular expressions to exclude struct packages and names from check. - # Default: [] - exclude: - # std libs - - "^net/http.Client$" - - "^net/http.Cookie$" - - "^net/http.Request$" - - "^net/http.Response$" - - "^net/http.Server$" - - "^net/http.Transport$" - - "^net/url.URL$" - - "^os/exec.Cmd$" - - "^reflect.StructField$" - # public libs - - "^github.com/Shopify/sarama.Config$" - - "^github.com/Shopify/sarama.ProducerMessage$" - - "^github.com/mitchellh/mapstructure.DecoderConfig$" - - "^github.com/prometheus/client_golang/.+Opts$" - - "^github.com/spf13/cobra.Command$" - - "^github.com/spf13/cobra.CompletionOptions$" - - "^github.com/stretchr/testify/mock.Mock$" - - "^github.com/testcontainers/testcontainers-go.+Request$" - - "^github.com/testcontainers/testcontainers-go.FromDockerfile$" - - "^golang.org/x/tools/go/analysis.Analyzer$" - - "^google.golang.org/protobuf/.+Options$" - - "^gopkg.in/yaml.v3.Node$" - - funlen: - # Checks the number of lines in a function. - # If lower than 0, disable the check. - # Default: 60 - lines: 100 - # Checks the number of statements in a function. - # If lower than 0, disable the check. - # Default: 40 - statements: 50 - - gocognit: - # Minimal code complexity to report. - # Default: 30 (but we recommend 10-20) - min-complexity: 20 - - gocritic: - # Settings passed to gocritic. - # The settings key is the name of a supported gocritic checker. - # The list of supported checkers can be find in https://go-critic.github.io/overview. - settings: - captLocal: - # Whether to restrict checker to params only. - # Default: true - paramsOnly: false - underef: - # Whether to skip (*x).method() calls where x is a pointer receiver. - # Default: true - skipRecvDeref: false - - mnd: - # List of function patterns to exclude from analysis. - # Values always ignored: `time.Date`, - # `strconv.FormatInt`, `strconv.FormatUint`, `strconv.FormatFloat`, - # `strconv.ParseInt`, `strconv.ParseUint`, `strconv.ParseFloat`. - # Default: [] - ignored-files: - - 'pkg(/|\\)settings(/|\\)blockchain_settings\.go$' # using both file path separators to support Windows - ignored-functions: - - os.Chmod - - os.Mkdir - - os.MkdirAll - - os.OpenFile - - os.WriteFile - - prometheus.ExponentialBuckets - - prometheus.ExponentialBucketsRange - - prometheus.LinearBuckets - ignored-numbers: [ 0, 1, 2 ] - - gomodguard: - blocked: - # List of blocked modules. - # Default: [] - modules: - - github.com/golang/protobuf: - recommendations: - - google.golang.org/protobuf - reason: "see https://developers.google.com/protocol-buffers/docs/reference/go/faq#modules" - - github.com/satori/go.uuid: - recommendations: - - github.com/google/uuid - reason: "satori's package is not maintained" - - github.com/gofrs/uuid: - recommendations: - - github.com/google/uuid - reason: "gofrs' package is not go module" - - govet: - # Enable all analyzers. - # Default: false - enable-all: true - # Disable analyzers by name. - # Run `go tool vet help` to see all analyzers. - # Default: [] - disable: - - fieldalignment # too strict - # Settings per analyzer. - settings: - shadow: - # Whether to be strict about shadowing; can be noisy. - # Default: false - strict: true - - nakedret: - # Make an issue if func has more lines of code than this setting, and it has naked returns. - # Default: 30 - max-func-lines: 0 - - nolintlint: - # Exclude following linters from requiring an explanation. - # Default: [] - allow-no-explanation: [ funlen, gocognit, lll ] - # Enable to require an explanation of nonzero length after each nolint directive. - # Default: false - require-explanation: true - # Enable to require nolint directives to mention the specific linter being suppressed. - # Default: false - require-specific: true - - rowserrcheck: - # database/sql is always checked - # Default: [] - packages: - - github.com/jmoiron/sqlx - - tenv: - # The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures. - # Otherwise, only methods that take `*testing.T`, `*testing.B`, and `testing.TB` as arguments are checked. - # Default: false - all: true - +version: "2" linters: - disable-all: true + default: none enable: - ## enabled by default - - errcheck # checking for unchecked errors, these unchecked errors can be critical bugs in some cases - - gosimple # specializes in simplifying a code - - govet # reports suspicious constructs, such as Printf calls whose arguments do not align with the format string - - ineffassign # detects when assignments to existing variables are not used - - staticcheck # is a go vet on steroids, applying a ton of static analysis checks - - typecheck # like the front-end of a Go compiler, parses and type-checks Go code - - unused # checks for unused constants, variables, functions and types - ## disabled by default - - asasalint # checks for pass []any as any in variadic func(...any) - - asciicheck # checks that your code does not contain non-ASCII identifiers - - bidichk # checks for dangerous unicode character sequences - - bodyclose # checks whether HTTP response body is closed successfully - - cyclop # checks function and package cyclomatic complexity - - dupl # tool for code clone detection - - durationcheck # checks for two durations multiplied together - - errname # checks that sentinel errors are prefixed with the Err and error types are suffixed with the Error - - errorlint # finds code that will cause problems with the error wrapping scheme introduced in Go 1.13 - - exhaustive # checks exhaustiveness of enum switch statements - - forbidigo # forbids identifiers - - funlen # tool for detection of long functions - - gocheckcompilerdirectives # validates go compiler directive comments (//go:) - - gochecknoglobals # checks that no global variables exist - - gochecknoinits # checks that no init functions are present in Go code - - gocognit # computes and checks the cognitive complexity of functions - - goconst # finds repeated strings that could be replaced by a constant - - gocritic # provides diagnostics that check for bugs, performance and style issues - - gocyclo # computes and checks the cyclomatic complexity of functions - - godot # checks if comments end in a period - - goimports # in addition to fixing imports, goimports also formats your code in the same style as gofmt - - mnd # detects magic numbers - - gomoddirectives # manages the use of 'replace', 'retract', and 'excludes' directives in go.mod - - gomodguard # allow and block lists linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations - - goprintffuncname # checks that printf-like functions are named with f at the end - - gosec # inspects source code for security problems - - lll # reports long lines - - loggercheck # checks key value pairs for common logger libraries (kitlog,klog,logr,zap) - - makezero # finds slice declarations with non-zero initial length - - mirror # reports wrong mirror patterns of bytes/strings usage - - musttag # enforces field tags in (un)marshaled structs - - nakedret # finds naked returns in functions greater than a specified function length - - nestif # reports deeply nested if statements - - nilerr # finds the code that returns nil even if it checks that the error is not nil - - nilnil # checks that there is no simultaneous return of nil error and an invalid value - - noctx # finds sending http request without context.Context - - nolintlint # reports ill-formed or insufficient nolint directives - - nonamedreturns # reports all named returns - - nosprintfhostport # checks for misuse of Sprintf to construct a host with port in a URL - - predeclared # finds code that shadows one of Go's predeclared identifiers - - promlinter # checks Prometheus metrics naming via promlint - - reassign # checks that package variables are not reassigned - - revive # fast, configurable, extensible, flexible, and beautiful linter for Go, drop-in replacement of golint - - rowserrcheck # checks whether Err of rows is checked successfully - - sqlclosecheck # checks that sql.Rows and sql.Stmt are closed - - stylecheck # is a replacement for golint - - tenv # detects using os.Setenv instead of t.Setenv since Go1.17 - - testableexamples # checks if examples are testable (have an expected output) - - testpackage # makes you use a separate _test package - - tparallel # detects inappropriate usage of t.Parallel() method in your Go test codes - - unconvert # removes unnecessary type conversions - - unparam # reports unused function parameters - - usestdlibvars # detects the possibility to use variables/constants from the Go standard library - - wastedassign # finds wasted assignment statements - - whitespace # detects leading and trailing whitespace - - copyloopvar # detects places where loop variables are copied - - ## you may want to enable - #- decorder # checks declaration order and count of types, constants, variables and functions - #- exhaustruct # [highly recommend to enable] checks if all structure fields are initialized - #- gci # controls golang package import order and makes it always deterministic - #- ginkgolinter # [if you use ginkgo/gomega] enforces standards of using ginkgo and gomega - #- godox # detects FIXME, TODO and other comment keywords - #- goheader # checks is file header matches to pattern - #- interfacebloat # checks the number of methods inside an interface - #- ireturn # accept interfaces, return concrete types - #- prealloc # [premature optimization, but can be used in some cases] finds slice declarations that could potentially be preallocated - #- tagalign # checks that struct tags are well aligned - #- varnamelen # [great idea, but too many false positives] checks that the length of a variable's name matches its scope - #- wrapcheck # checks that errors returned from external packages are wrapped - #- zerologlint # detects the wrong usage of zerolog that a user forgets to dispatch zerolog.Event - - ## disabled - #- containedctx # detects struct contained context.Context field - #- contextcheck # [too many false positives] checks the function whether use a non-inherited context - #- depguard # [replaced by gomodguard] checks if package imports are in a list of acceptable packages - #- dogsled # checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) - #- dupword # [useless without config] checks for duplicate words in the source code - #- errchkjson # [don't see profit + I'm against of omitting errors like in the first example https://github.com/breml/errchkjson] checks types passed to the json encoding functions. Reports unsupported types and optionally reports occasions, where the check for the returned error can be omitted - #- forcetypeassert # [replaced by errcheck] finds forced type assertions - #- goerr113 # [too strict] checks the errors handling expressions - #- gofmt # [replaced by goimports] checks whether code was gofmt-ed - #- gofumpt # [replaced by goimports, gofumports is not available yet] checks whether code was gofumpt-ed - #- gosmopolitan # reports certain i18n/l10n anti-patterns in your Go codebase - #- grouper # analyzes expression groups - #- importas # enforces consistent import aliases - #- maintidx # measures the maintainability index of each function - #- misspell # [useless] finds commonly misspelled English words in comments - #- nlreturn # [too strict and mostly code is not more readable] checks for a new line before return and branch statements to increase code clarity - #- paralleltest # [too many false positives] detects missing usage of t.Parallel() method in your Go test - #- tagliatelle # checks the struct tags - #- thelper # detects golang test helpers without t.Helper() call and checks the consistency of test helpers - #- wsl # [too strict and mostly code is not more readable] whitespace linter forces you to use empty lines - - ## deprecated - #- deadcode # [deprecated, replaced by unused] finds unused code - #- exhaustivestruct # [deprecated, replaced by exhaustruct] checks if all struct's fields are initialized - #- golint # [deprecated, replaced by revive] golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes - #- ifshort # [deprecated] checks that your code uses short syntax for if-statements whenever possible - #- interfacer # [deprecated] suggests narrower interface types - #- maligned # [deprecated, replaced by govet fieldalignment] detects Go structs that would take less memory if their fields were sorted - #- nosnakecase # [deprecated, replaced by revive var-naming] detects snake case of variable naming and function name - #- structcheck # [deprecated, replaced by unused] finds unused struct fields - #- varcheck # [deprecated, replaced by unused] finds unused global variables and constants - - + - asasalint + - asciicheck + - bidichk + - bodyclose + - copyloopvar + - cyclop + - dupl + - durationcheck + - errcheck + - errname + - errorlint + - exhaustive + - forbidigo + - funlen + - gocheckcompilerdirectives + - gochecknoglobals + - gochecknoinits + - gocognit + - goconst + - gocritic + - gocyclo + - godot + - gomoddirectives + - gomodguard_v2 + - goprintffuncname + - gosec + - govet + - ineffassign + - lll + - loggercheck + - makezero + - mirror + - mnd + - musttag + - nakedret + - nestif + - nilerr + - nilnil + - noctx + - nolintlint + - nonamedreturns + - nosprintfhostport + - predeclared + - promlinter + - reassign + - revive + - rowserrcheck + - sqlclosecheck + - staticcheck + - testableexamples + - testpackage + - tparallel + - unconvert + - unparam + - unused + - usestdlibvars + - usetesting + - wastedassign + - whitespace + - sloglint + settings: + cyclop: + max-complexity: 30 + package-average: 10 + errcheck: + check-type-assertions: true + exhaustive: + check: + - switch + - map + exhaustruct: + exclude: + - ^net/http.Client$ + - ^net/http.Cookie$ + - ^net/http.Request$ + - ^net/http.Response$ + - ^net/http.Server$ + - ^net/http.Transport$ + - ^net/url.URL$ + - ^os/exec.Cmd$ + - ^reflect.StructField$ + - ^github.com/Shopify/sarama.Config$ + - ^github.com/Shopify/sarama.ProducerMessage$ + - ^github.com/mitchellh/mapstructure.DecoderConfig$ + - ^github.com/prometheus/client_golang/.+Opts$ + - ^github.com/spf13/cobra.Command$ + - ^github.com/spf13/cobra.CompletionOptions$ + - ^github.com/stretchr/testify/mock.Mock$ + - ^github.com/testcontainers/testcontainers-go.+Request$ + - ^github.com/testcontainers/testcontainers-go.FromDockerfile$ + - ^golang.org/x/tools/go/analysis.Analyzer$ + - ^google.golang.org/protobuf/.+Options$ + - ^gopkg.in/yaml.v3.Node$ + funlen: + lines: 100 + statements: 50 + gocognit: + min-complexity: 20 + gocritic: + settings: + captLocal: + paramsOnly: false + underef: + skipRecvDeref: false + gomodguard_v2: + blocked: + - module: github.com/golang/protobuf + recommendations: + - google.golang.org/protobuf + reason: see https://developers.google.com/protocol-buffers/docs/reference/go/faq#modules + - module: github.com/satori/go.uuid + recommendations: + - github.com/google/uuid + reason: satori's package is not maintained + - module: github.com/gofrs/uuid + recommendations: + - github.com/google/uuid + reason: gofrs' package is not go module + govet: + disable: + - fieldalignment + enable-all: true + settings: + shadow: + strict: true + mnd: + ignored-numbers: + - "0" + - "1" + - "2" + ignored-functions: + - os.Chmod + - os.Mkdir + - os.MkdirAll + - os.OpenFile + - os.WriteFile + - prometheus.ExponentialBuckets + - prometheus.ExponentialBucketsRange + - prometheus.LinearBuckets + nolintlint: + require-explanation: true + require-specific: true + allow-no-explanation: + - funlen + - gocognit + - lll + rowserrcheck: + packages: + - github.com/jmoiron/sqlx + usetesting: + os-setenv: true + revive: + rules: + - name: var-naming + disabled: true + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + rules: + - linters: + - godot + source: (noinspection|TODO) + - linters: + - gocritic + source: //noinspection + - linters: + - bodyclose + - dupl + - funlen + - goconst + - gosec + - noctx + - wrapcheck + path: _test\.go + - text: "(slog)\\.\\w+" + linters: + - noctx + paths: + - third_party$ + - builtin$ + - examples$ issues: - # Maximum count of issues with the same text. - # Set to 0 to disable. - # Default: 3 max-same-issues: 50 - - exclude-rules: - - source: "(noinspection|TODO)" - linters: [ godot ] - - source: "//noinspection" - linters: [ gocritic ] - - path: "_test\\.go" - linters: - - bodyclose - - dupl - - funlen - - goconst - - gosec - - noctx - - wrapcheck +formatters: + enable: + - goimports + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/README.md b/README.md index 664aeab..73e5a74 100644 --- a/README.md +++ b/README.md @@ -363,7 +363,10 @@ This command will start Fork Detector to work with the `MainNet` network in outg Available command-line parameters: * `-db` - path to the directory where the database will be stored. **This parameter is required.** -* `-log-level` - logging level, default is `INFO`. Other possible values: `DEBUG`, `INFO`, `WARN`, `ERROR`, and `FATAL`. +* `-log-level` - logging level, default is `info`. Other possible values: `debug`, `info`, `warn`, and `error`. +* `-log-type` - logger output format, default is `pretty`. Other possible values: `text` and `json`. +* `-log-network` - log the operation of network stack, turned off by default. +* `-log-network-data` - log network messages as Base64 strings, turned off by default. * `-blockchain-type` - network type, available values are `mainnet`, `testnet`, and `stagenet`. Default is `mainnet`. * `-peers` - a comma-separated list of node addresses to initially connect to in the format `ip:port,...,ip:port`. **This parameter is required.** * `-api` - the address at which the Fork Detector API will be available. The default is `localhost:8080`, meaning the API will be available locally on port `8080`. diff --git a/api/api.go b/api/api.go index 7c4e67d..f7487ce 100644 --- a/api/api.go +++ b/api/api.go @@ -8,8 +8,10 @@ import ( "errors" "fmt" "io/fs" + "log/slog" "net/http" "net/netip" + "os" "runtime" "sort" "strconv" @@ -18,8 +20,8 @@ import ( "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" + "github.com/wavesplatform/gowaves/pkg/logging" "github.com/wavesplatform/gowaves/pkg/proto" - "go.uber.org/zap" "golang.org/x/sync/errgroup" "github.com/alexeykiselev/waves-fork-detector/chains" @@ -37,7 +39,7 @@ var ( // Logger is a middleware that logs the start and end of each request, along // with some useful data about what was requested, what the response status was, // and how long it took to return. -func Logger(l *zap.Logger) func(next http.Handler) http.Handler { +func Logger(l *slog.Logger) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor) @@ -45,13 +47,13 @@ func Logger(l *zap.Logger) func(next http.Handler) http.Handler { t1 := time.Now() defer func() { l.Debug("Served", - zap.String("proto", r.Proto), - zap.String("path", r.URL.Path), - zap.String("remote", r.RemoteAddr), - zap.Duration("lat", time.Since(t1)), - zap.Int("status", ww.Status()), - zap.Int("size", ww.BytesWritten()), - zap.String("reqId", middleware.GetReqID(r.Context()))) + slog.String("proto", r.Proto), + slog.String("path", r.URL.Path), + slog.String("remote", r.RemoteAddr), + slog.Duration("lat", time.Since(t1)), + slog.Int("status", ww.Status()), + slog.Int("size", ww.BytesWritten()), + slog.String("reqId", middleware.GetReqID(r.Context()))) }() next.ServeHTTP(ww, r) @@ -66,9 +68,12 @@ type API struct { registry *peers.Registry linkage *chains.Linkage srv *http.Server + logger *slog.Logger } -func NewAPI(registry *peers.Registry, linkage *chains.Linkage, bind string) (*API, error) { +func NewAPI( + registry *peers.Registry, linkage *chains.Linkage, bind string, logger *slog.Logger, +) (*API, error) { if bind == "" { return nil, errors.New("empty address to bin") } @@ -77,11 +82,10 @@ func NewAPI(registry *peers.Registry, linkage *chains.Linkage, bind string) (*AP return nil, fmt.Errorf("failed to get swagger FS: %w", err) } - a := API{registry: registry, linkage: linkage} + a := API{registry: registry, linkage: linkage, logger: logger} r := chi.NewRouter() r.Use(middleware.RequestID) - r.Use(middleware.RealIP) - r.Use(Logger(zap.L())) + r.Use(Logger(logger)) r.Use(middleware.Recoverer) r.Use(middleware.Compress(flate.DefaultCompression)) const apiRoot = "/api" @@ -100,19 +104,19 @@ func (a *API) Run(ctx context.Context) { func (a *API) Shutdown() { if err := a.srv.Shutdown(a.ctx); err != nil && !errors.Is(err, context.Canceled) { - zap.S().Errorf("Failed to shutdown API: %v", err) + a.logger.Error("Failed to shutdown API", logging.Error(err)) } if err := a.wait(); err != nil { - zap.S().Warnf("Failed to shutdown API: %v", err) + a.logger.Warn("Failed to shutdown API", logging.Error(err)) } - zap.S().Info("API shutdown successfully") + a.logger.Info("API shutdown successfully") } func (a *API) runServer() error { err := a.srv.ListenAndServe() if err != nil && !errors.Is(err, http.ErrServerClosed) { - zap.S().Fatalf("Failed to start API: %v", err) - return err + a.logger.Error("Failed to start API", logging.Error(err)) + os.Exit(1) } return nil } diff --git a/chains/linkage.go b/chains/linkage.go index 102f90a..8fe2faf 100644 --- a/chains/linkage.go +++ b/chains/linkage.go @@ -3,6 +3,7 @@ package chains import ( "errors" "fmt" + "log/slog" "math" "math/big" "net/netip" @@ -12,8 +13,8 @@ import ( "time" "github.com/syndtr/goleveldb/leveldb" + "github.com/wavesplatform/gowaves/pkg/logging" "github.com/wavesplatform/gowaves/pkg/proto" - "go.uber.org/zap" ) const ( @@ -44,9 +45,11 @@ type Linkage struct { mu *sync.RWMutex st *storage + + logger *slog.Logger } -func NewLinkage(path string, scheme proto.Scheme, genesis proto.Block) (*Linkage, error) { +func NewLinkage(path string, scheme proto.Scheme, genesis proto.Block, logger *slog.Logger) (*Linkage, error) { st, err := newStorage(path, scheme) if err != nil { return nil, fmt.Errorf("failed to initialize Linkage: %w", err) @@ -59,13 +62,14 @@ func NewLinkage(path string, scheme proto.Scheme, genesis proto.Block) (*Linkage genesis: genesis.BlockID(), mu: &sync.RWMutex{}, st: st, + logger: logger, }, nil } func (l *Linkage) Close() { err := l.st.close() if err != nil { - zap.S().Errorf("Failed to close Linkage: %v", err) + l.logger.Error("Failed to close Linkage", logging.Error(err)) } } @@ -234,26 +238,26 @@ func (l *Linkage) LogInitialStats() { heads, err := l.activeHeads() if err != nil { - zap.S().Errorf("Failed to log statistics: %v", err) + l.logger.Error("Failed to log statistics", logging.Error(err)) return } - zap.S().Infof("Heads count in storage: %d", len(heads)) + l.logger.Info("Heads in storage", slog.Int("count", len(heads))) for _, head := range heads { b, blErr := l.Block(head.BlockID) if blErr != nil { - zap.S().Errorf("Failed to get block: %v", blErr) + l.logger.Error("Failed to get block", logging.Error(blErr)) return } - zap.S().Infof("\tHead '%s' at height %d", head.BlockID.String(), b.Height) + l.logger.Info("Head", slog.String("block", head.BlockID.String()), slog.Uint64("height", uint64(b.Height))) } leashes, err := l.st.leashes() if err != nil { - zap.S().Errorf("Failed to log statistics: %v", err) + l.logger.Error("Failed to log statistics", logging.Error(err)) return } - zap.S().Infof("Leashes count in storage: %d", len(leashes)) + l.logger.Info("Leashes in storage", slog.Int("count", len(leashes))) for _, lsh := range leashes { - zap.S().Infof("\tPeer '%s' on block '%s'", lsh.Addr.String(), lsh.BlockID.String()) + l.logger.Info("Leash", slog.String("peer", lsh.Addr.String()), slog.String("block", lsh.BlockID.String())) } } diff --git a/chains/linkage_internal_test.go b/chains/linkage_internal_test.go index cafcd68..754d951 100644 --- a/chains/linkage_internal_test.go +++ b/chains/linkage_internal_test.go @@ -2,6 +2,7 @@ package chains import ( "fmt" + "log/slog" "net/netip" "testing" @@ -259,8 +260,8 @@ func TestLastBlockIDs(t *testing.T) { } func createTestLinkageAndGenesisID(t testing.TB) (*Linkage, proto.BlockID) { - genesis := settings.TestNetSettings.Genesis - dr, err := NewLinkage(t.TempDir(), proto.TestNetScheme, genesis) + genesis := settings.MustTestNetSettings().Genesis + dr, err := NewLinkage(t.TempDir(), proto.TestNetScheme, genesis, slog.New(slog.DiscardHandler)) require.NoError(t, err) return dr, genesis.BlockID() } diff --git a/chains/storage_internal_test.go b/chains/storage_internal_test.go index 92af8c0..4eb6ba7 100644 --- a/chains/storage_internal_test.go +++ b/chains/storage_internal_test.go @@ -13,7 +13,7 @@ import ( func TestDoubleInitialization(t *testing.T) { st, _ := createTestStorage(t) - genesis := settings.TestNetSettings.Genesis + genesis := settings.MustTestNetSettings().Genesis err := st.initialize(genesis) require.NoError(t, err) bl, err := st.block(genesis.BlockID()) @@ -367,7 +367,7 @@ func BenchmarkLCAOnTwo1MBlockForks(b *testing.B) { func createTestStorage(t testing.TB) (*storage, proto.BlockID) { st, err := newStorage(t.TempDir(), proto.TestNetScheme) require.NoError(t, err) - genesis := settings.TestNetSettings.Genesis + genesis := settings.MustTestNetSettings().Genesis err = st.initialize(genesis) require.NoError(t, err) return st, genesis.BlockID() diff --git a/connection_manager.go b/connection_manager.go index 64f7ee6..36b5dad 100644 --- a/connection_manager.go +++ b/connection_manager.go @@ -3,11 +3,10 @@ package main import ( "context" "fmt" + "log/slog" "net" "net/netip" - "go.uber.org/zap" - "github.com/wavesplatform/gowaves/pkg/p2p/incoming" "github.com/wavesplatform/gowaves/pkg/p2p/outgoing" "github.com/wavesplatform/gowaves/pkg/p2p/peer" @@ -28,10 +27,14 @@ type ConnectionManager struct { parent peer.Parent vp peers.VersionProvider discarded map[proto.PeerMessageID]bool + logger *slog.Logger + nl *slog.Logger // Network logger. + ndl *slog.Logger // Network data logger. } func NewConnectionManager( scheme byte, name string, nonce uint32, declared proto.TCPAddr, vp peers.VersionProvider, parent peer.Parent, + logger, nl, ndl *slog.Logger, ) *ConnectionManager { discardedMessages := map[proto.PeerMessageID]bool{ proto.ContentIDGetPeers: false, @@ -54,6 +57,7 @@ func NewConnectionManager( proto.ContentIDMicroBlockSnapshot: true, proto.ContentIDBlockSnapshot: true, proto.ContentIDMicroBlockSnapshotRequest: true, + proto.ContentIDEndorseBlock: true, } return &ConnectionManager{ network: fmt.Sprintf("%s%c", defaultApplication, scheme), @@ -63,11 +67,14 @@ func NewConnectionManager( parent: parent, vp: vp, discarded: discardedMessages, + logger: logger, + nl: nl, + ndl: ndl, } } func (h *ConnectionManager) Accept(ctx context.Context, conn net.Conn) error { - zap.S().Debugf("[CON] New incoming connection from %s", conn.RemoteAddr()) + h.logger.Debug("New incoming connection", slog.String("remote", conn.RemoteAddr().String())) ap, err := netip.ParseAddrPort(conn.RemoteAddr().String()) if err != nil { @@ -88,11 +95,11 @@ func (h *ConnectionManager) Accept(ctx context.Context, conn net.Conn) error { Version: ver, } - return incoming.RunIncomingPeer(ctx, params) + return incoming.RunIncomingPeer(ctx, params, h.nl, h.ndl) } func (h *ConnectionManager) Connect(ctx context.Context, addr proto.TCPAddr) error { - zap.S().Debugf("[CON] New outgoing connection to %s", addr) + h.logger.Debug("New outgoing connection", slog.String("address", addr.String())) params := outgoing.EstablishParams{ Address: addr, WavesNetwork: h.network, @@ -110,7 +117,7 @@ func (h *ConnectionManager) Connect(ctx context.Context, addr proto.TCPAddr) err if err != nil { return err } - return outgoing.EstablishConnection(ctx, params, ver) + return outgoing.EstablishConnection(ctx, params, ver, h.nl, h.ndl) } func (h *ConnectionManager) skipFunc(header proto.Header) bool { diff --git a/distributor.go b/distributor.go index 2807b88..2b155dd 100644 --- a/distributor.go +++ b/distributor.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "log/slog" "math/big" "net" "net/netip" @@ -11,9 +12,9 @@ import ( "github.com/rhansen/go-kairos/kairos" "github.com/wavesplatform/gowaves/pkg/crypto" - "go.uber.org/zap" "golang.org/x/sync/errgroup" + "github.com/wavesplatform/gowaves/pkg/logging" "github.com/wavesplatform/gowaves/pkg/p2p/peer" "github.com/wavesplatform/gowaves/pkg/proto" @@ -37,10 +38,12 @@ type Distributor struct { blockCh chan loading.BlockPackage timer *kairos.Timer + + logger *slog.Logger } func NewDistributor( - scheme proto.Scheme, linkage *chains.Linkage, registry *peers.Registry, parent peer.Parent, + scheme proto.Scheme, linkage *chains.Linkage, registry *peers.Registry, parent peer.Parent, logger *slog.Logger, ) *Distributor { idsCh := make(chan loading.IDsPackage) blockCh := make(chan loading.BlockPackage) @@ -52,6 +55,7 @@ func NewDistributor( idsCh: idsCh, blockCh: blockCh, timer: kairos.NewStoppedTimer(), + logger: logger, } } @@ -66,11 +70,11 @@ func (d *Distributor) Run(ctx context.Context) { func (d *Distributor) Shutdown() { if err := d.wait(); err != nil { - zap.S().Warnf("Failed to shutdown Distributor: %v", err) + d.logger.Warn("Failed to shutdown Distributor", logging.Error(err)) } close(d.idsCh) close(d.blockCh) - zap.S().Info("Distributor shutdown successfully") + d.logger.Info("Distributor shutdown successfully") } func (d *Distributor) IDsCh() <-chan loading.IDsPackage { @@ -85,10 +89,10 @@ func (d *Distributor) runLoop() error { for { select { case <-d.ctx.Done(): - zap.S().Debugf("[DTR] Distributor shutdown in progress...") + d.logger.Debug("Distributor shutdown in progress...") return nil case <-d.timer.C: - zap.S().Info("Pinging connections") + d.logger.Info("Pinging connections") d.pingConnections() d.timer.Reset(pingInterval) case infoMessage := <-d.parent.InfoCh: @@ -115,11 +119,11 @@ func (d *Distributor) handleInfoMessage(msg peer.InfoMessage) { func (d *Distributor) handleConnected(cm *peer.Connected) { ap, err := netip.ParseAddrPort(cm.Peer.RemoteAddr().String()) if err != nil { - zap.S().Warnf("Failed to parse address: %v", err) + d.logger.Warn("Failed to parse address", logging.Error(err)) return } if rpErr := d.registry.RegisterPeer(ap.Addr(), cm.Peer, cm.Peer.Handshake()); rpErr != nil { - zap.S().Warnf("Failed to check peer: %v", rpErr) + d.logger.Warn("Failed to check peer", logging.Error(rpErr)) return } } @@ -127,16 +131,16 @@ func (d *Distributor) handleConnected(cm *peer.Connected) { func (d *Distributor) handleInternalError(peer peer.Peer, ie *peer.InternalErr) { ap, err := netip.ParseAddrPort(peer.RemoteAddr().String()) if err != nil { - zap.S().Warnf("Failed to parse address: %v", err) + d.logger.Warn("Failed to parse address", logging.Error(err)) return } - zap.S().Infof("[DTR] Closing connection with peer %s", ap.Addr().String()) - zap.S().Debugf("[DTR] Peer %s failed with error: %v", ap.Addr().String(), ie.Err) + d.logger.Info("Closing connection with peer", slog.String("peer", ap.Addr().String())) + d.logger.Debug("Peer failed with error", slog.String("peer", ap.Addr().String()), logging.Error(ie.Err)) if clErr := peer.Close(); clErr != nil { - zap.S().Warnf("Failed to close peer '%s' connection: %v", ap.Addr().String(), clErr) + d.logger.Warn("Failed to close peer connection", slog.String("peer", ap.Addr().String()), logging.Error(clErr)) } if urErr := d.registry.UnregisterPeer(ap.Addr()); urErr != nil { - zap.S().Warnf("Failed to unregister peer '%s': %v", ap.Addr().String(), urErr) + d.logger.Warn("Failed to unregister peer", slog.String("peer", ap.Addr().String()), logging.Error(urErr)) } } @@ -156,7 +160,7 @@ func (d *Distributor) handleMessage(msg peer.ProtoMessage) { d.handleMicroBlockInvMessage(msg.ID, v) case *proto.PBBlockMessage: d.handleProtoBlockMessage(msg.ID, v) - case *proto.BlockIdsMessage: + case *proto.BlockIDsMessage: d.handleBlockIDsMessage(msg.ID, v.Blocks) } } @@ -164,14 +168,14 @@ func (d *Distributor) handleMessage(msg peer.ProtoMessage) { func (d *Distributor) handleScoreMessage(peer peer.Peer, score []byte) { ap, err := netip.ParseAddrPort(peer.RemoteAddr().String()) if err != nil { - zap.S().Debugf("[DTR] Failed to parse peer address: %v", err) + d.logger.Debug("Failed to parse peer address", logging.Error(err)) return } s := big.NewInt(0).SetBytes(score) - zap.S().Debugf("[DTR] New score %s recevied from %s", s.String(), ap.Addr().String()) + d.logger.Debug("New score received", slog.String("score", s.String()), slog.String("peer", ap.Addr().String())) err = d.registry.UpdatePeerScore(ap.Addr(), s) if err != nil { - zap.S().Debugf("[DTR] Failed to update score of peer '%s': %v", ap.Addr().String(), err) + d.logger.Debug("Failed to update score of peer", slog.String("peer", ap.Addr().String()), logging.Error(err)) return } } @@ -179,25 +183,28 @@ func (d *Distributor) handleScoreMessage(peer peer.Peer, score []byte) { func (d *Distributor) handleBlockMessage(peer peer.Peer, bm *proto.BlockMessage) { b := &proto.Block{} if err := b.UnmarshalBinary(bm.BlockBytes, d.scheme); err != nil { - zap.S().Warnf("Failed to unmarshal block from peer '%s': %v", peer.RemoteAddr().String(), err) + d.logger.Warn("Failed to unmarshal block from peer", slog.String("peer", peer.RemoteAddr().String()), + logging.Error(err)) return } - zap.S().Infof("Block '%s' received from peer '%s'", b.BlockID().String(), peer.RemoteAddr().String()) + d.logger.Info("Block received from peer", slog.String("block", b.BlockID().String()), + slog.String("peer", peer.RemoteAddr().String())) if err := d.handleBlock(b, peer.RemoteAddr()); err != nil { - zap.S().Warnf("Failed to handle block from peer '%s': %v", peer.RemoteAddr().String(), err) + d.logger.Warn("Failed to handle block from peer", slog.String("peer", peer.RemoteAddr().String()), logging.Error(err)) } } func (d *Distributor) handleProtoBlockMessage(peer peer.Peer, bm *proto.PBBlockMessage) { b := &proto.Block{} if err := b.UnmarshalFromProtobuf(bm.PBBlockBytes); err != nil { - zap.S().Warnf("Failed to unmarshal protobuf block from peer '%s': %v", - peer.RemoteAddr().String(), err) + d.logger.Warn("Failed to unmarshal protobuf block from peer", slog.String("peer", peer.RemoteAddr().String()), + logging.Error(err)) return } - zap.S().Infof("Block '%s' received from peer '%s'", b.BlockID().String(), peer.RemoteAddr().String()) + d.logger.Info("Block received from peer", slog.String("block", b.BlockID().String()), + slog.String("peer", peer.RemoteAddr().String())) if err := d.handleBlock(b, peer.RemoteAddr()); err != nil { - zap.S().Warnf("Failed to handle block from peer '%s': %v", peer.RemoteAddr().String(), err) + d.logger.Warn("Failed to handle block from peer", slog.String("peer", peer.RemoteAddr().String()), logging.Error(err)) } } @@ -209,33 +216,34 @@ func (d *Distributor) handleBlock(block *proto.Block, addr proto.TCPAddr) error if putErr := d.linkage.PutBlock(block, ap.Addr()); putErr != nil && !errors.Is(putErr, chains.ErrParentNotFound) { return fmt.Errorf("failed to append block: %w", err) } - zap.S().Debugf("[DTR] Block '%s' from '%s' was appended", block.BlockID().String(), ap.Addr().String()) + d.logger.Debug("Block was appended", slog.String("block", block.BlockID().String()), + slog.String("peer", ap.Addr().String())) d.blockCh <- loading.BlockPackage{Peer: ap.Addr(), Block: block} return nil } func (d *Distributor) handlePeersMessage(pm *proto.PeersMessage) { - zap.S().Debugf("[DTR] Received %d peers", len(pm.Peers)) + d.logger.Debug("Peers received", slog.Int("count", len(pm.Peers))) addresses := make([]*net.TCPAddr, 0, len(pm.Peers)) for _, pi := range pm.Peers { ap, err := netip.ParseAddrPort(pi.String()) if err != nil { - zap.S().Debugf("[DTR] Failed to parse peer address: %v", err) + d.logger.Debug("Failed to parse peer address", logging.Error(err)) continue } addresses = append(addresses, net.TCPAddrFromAddrPort(ap)) } cnt := d.registry.AppendAddresses(addresses) if cnt > 0 { - zap.S().Infof("[DTR] Added %d new peers", cnt) + d.logger.Info("New peers added", slog.Int("count", cnt)) } } func (d *Distributor) handleGetPeersMessage(peer peer.Peer) { - zap.S().Debugf("[DTR] Get peers from %s", peer.RemoteAddr().String()) + d.logger.Debug("Get peers request received", slog.String("peer", peer.RemoteAddr().String())) friendlyPeers, err := d.registry.FriendlyPeers() if err != nil { - zap.S().Warnf("Failed to get peers: %v", err) + d.logger.Warn("Failed to get peers", logging.Error(err)) return } infos := make([]proto.PeerInfo, 0, len(friendlyPeers)) @@ -255,47 +263,48 @@ func (d *Distributor) handleGetPeersMessage(peer peer.Peer) { func (d *Distributor) handleSignaturesMessage(peer peer.Peer, signatures []crypto.Signature) { ap, err := netip.ParseAddrPort(peer.RemoteAddr().String()) if err != nil { - zap.S().Warnf("Failed to parse peer address: %v", err) + d.logger.Warn("Failed to parse peer address", logging.Error(err)) return } ids := make([]proto.BlockID, len(signatures)) for i, s := range signatures { ids[i] = proto.NewBlockIDFromSignature(s) } - zap.S().Debugf("[DTR] Signatures received from '%s'", ap.Addr().String()) + d.logger.Debug("Signatures received", slog.String("peer", ap.Addr().String())) d.idsCh <- loading.IDsPackage{Peer: ap.Addr(), IDs: ids} } func (d *Distributor) handleBlockIDsMessage(peer peer.Peer, ids []proto.BlockID) { ap, err := netip.ParseAddrPort(peer.RemoteAddr().String()) if err != nil { - zap.S().Warnf("Failed to parse peer address: %v", err) + d.logger.Warn("Failed to parse peer address", logging.Error(err)) return } if len(ids) == 0 { - zap.S().Warnf("Empty IDs list received from '%s'", ap.Addr().String()) + d.logger.Warn("Empty IDs list received", slog.String("peer", ap.Addr().String())) return } - zap.S().Debugf("[DTR] Block IDs [%s..%s] received from %s", - ids[0].ShortString(), ids[len(ids)-1].ShortString(), ap.Addr().String()) + d.logger.Debug("Block IDs received", slog.String("first", ids[0].ShortString()), + slog.String("last", ids[len(ids)-1].ShortString()), slog.String("peer", ap.Addr().String())) d.idsCh <- loading.IDsPackage{Peer: ap.Addr(), IDs: ids} } func (d *Distributor) handleMicroBlockInvMessage(peer peer.Peer, msg *proto.MicroBlockInvMessage) { ap, err := netip.ParseAddrPort(peer.RemoteAddr().String()) if err != nil { - zap.S().Warnf("Failed to parse peer address: %v", err) + d.logger.Warn("Failed to parse peer address", logging.Error(err)) return } inv := &proto.MicroBlockInv{} if umErr := inv.UnmarshalBinary(msg.Body); umErr != nil { - zap.S().Warnf("Failed to unmarshal MicroBlockInv message: %v", umErr) + d.logger.Warn("Failed to unmarshal MicroBlockInv message", logging.Error(umErr)) return } if putErr := d.linkage.PutMicroBlock(inv, ap.Addr()); putErr != nil { - zap.S().Warnf("Failed to append micro-block '%s' received form '%s': %v", - inv.TotalBlockID.String(), ap.Addr().String(), putErr) + d.logger.Warn("Failed to append micro-block", slog.String("block", inv.TotalBlockID.String()), + slog.String("peer", ap.Addr().String()), logging.Error(putErr)) return } - zap.S().Infof("Micro-block '%s' received from peer '%s'", inv.TotalBlockID.String(), ap.Addr().String()) + d.logger.Info("Micro-block received from peer", slog.String("block", inv.TotalBlockID.String()), + slog.String("peer", ap.Addr().String())) } diff --git a/forkdetector.go b/forkdetector.go index 1bf471c..a7366f1 100644 --- a/forkdetector.go +++ b/forkdetector.go @@ -2,14 +2,11 @@ package main import ( "context" - "errors" "fmt" + "log/slog" "os" "os/signal" "syscall" - "unicode" - - "go.uber.org/zap" "github.com/wavesplatform/gowaves/pkg/logging" "github.com/wavesplatform/gowaves/pkg/p2p/peer" @@ -21,15 +18,29 @@ import ( "github.com/alexeykiselev/waves-fork-detector/version" ) +const ( + apiNamespace = "API" + connectionsNamespace = "CON" + distributorNamespace = "DTR" + linkageNamespace = "LNK" + listenerNamespace = "LSN" + loaderNamespace = "LDR" + netNamespace = "NET" + netDataNamespace = "NET.DATA" + registryNamespace = "REG" + respawnNamespace = "RSP" +) + func main() { + os.Exit(realMain()) // for more info see https://github.com/golang/go/issues/42078 +} + +func realMain() int { if err := run(); err != nil { - zap.S().Error(capitalize(err.Error())) - if _, errErr := fmt.Fprintf(os.Stderr, "%s\n", capitalize(err.Error())); errErr != nil { - return - } - os.Exit(1) + slog.Error("Failed to run Fork Detector", logging.Error(err)) + return 1 } - os.Exit(0) + return 0 } func run() error { @@ -38,35 +49,32 @@ func run() error { return err } - logger := logging.SetupLogger(p.logLevel, logging.NetworkDataFilter(false), logging.NetworkFilter(false)) - defer func() { - if syncErr := logger.Sync(); syncErr != nil && errors.Is(err, os.ErrInvalid) { - panic(fmt.Sprintf("Failed to close logging subsystem: %v\n", syncErr)) - } - }() + h := logging.DefaultHandler(p.lp) + slog.SetDefault(slog.New(h)) ctx, done := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer done() - zap.S().Infof("Waves Fork Detector %s", version.ForkDetectorVersion()) + slog.Info("Waves Fork Detector", slog.String("version", version.ForkDetectorVersion())) p.log() - reg, err := peers.NewRegistry(p.scheme, p.declaredAddress, p.versions, p.dbPath) + reg, err := peers.NewRegistry(p.scheme, p.declaredAddress, p.versions, p.dbPath, + newLogger(h, registryNamespace)) if err != nil { return fmt.Errorf("failed to create peers registry: %w", err) } defer func(reg *peers.Registry) { if rcErr := reg.Close(); rcErr != nil { - zap.S().Errorf("Failed to close peers registry: %v", rcErr) + slog.Error("Failed to close peers registry", logging.Error(rcErr)) } }(reg) n := reg.AppendAddresses(p.seedPeers) if n > 0 { - zap.S().Infof("%d seed peers added to storage", n) + slog.Info("Seed peers added to storage", slog.Int("count", n)) } - linkage, err := chains.NewLinkage(p.dbPath, p.scheme, p.genesis) + linkage, err := chains.NewLinkage(p.dbPath, p.scheme, p.genesis, newLogger(h, linkageNamespace)) if err != nil { return err } @@ -74,29 +82,33 @@ func run() error { linkage.LogInitialStats() - a, err := api.NewAPI(reg, linkage, p.apiBind) + a, err := api.NewAPI(reg, linkage, p.apiBind, newLogger(h, apiNamespace)) if err != nil { return fmt.Errorf("failed to create API server: %w", err) } a.Run(ctx) parent := peer.NewParent(true) - connManger := NewConnectionManager(p.scheme, p.name, p.nonce, p.declaredAddress, reg, parent) + nl := buildLogger(h, netNamespace, p.logNetwork) + ndl := buildLogger(h, netDataNamespace, p.logNetworkData) + connManger := NewConnectionManager(p.scheme, p.name, p.nonce, p.declaredAddress, reg, parent, + newLogger(h, connectionsNamespace), nl, ndl) - listener := NewListener(p.netBind, p.declaredAddress, connManger) + listener := NewListener(p.netBind, p.declaredAddress, connManger, newLogger(h, listenerNamespace)) listener.Run(ctx) - respawn := NewRespawn(reg, connManger) + respawn := NewRespawn(reg, connManger, newLogger(h, respawnNamespace)) respawn.Run(ctx) - distributor := NewDistributor(p.scheme, linkage, reg, parent) + distributor := NewDistributor(p.scheme, linkage, reg, parent, newLogger(h, distributorNamespace)) distributor.Run(ctx) - loader := loading.NewLoader(reg, linkage, distributor.IDsCh(), distributor.BlockCh()) + loader := loading.NewLoader(reg, linkage, distributor.IDsCh(), distributor.BlockCh(), + newLogger(h, loaderNamespace)) loader.Run(ctx) <-ctx.Done() - zap.S().Info("User termination in progress...") + slog.Info("User termination in progress...") a.Shutdown() listener.Shutdown() @@ -104,13 +116,18 @@ func run() error { loader.Shutdown() distributor.Shutdown() - zap.S().Info("Terminated") + slog.Info("Terminated") return nil } -func capitalize(str string) string { - runes := []rune(str) - runes[0] = unicode.ToUpper(runes[0]) - return string(runes) +func buildLogger(h slog.Handler, namespace string, enabled bool) *slog.Logger { + if !enabled { + return slog.New(slog.DiscardHandler) + } + return newLogger(h, namespace) +} + +func newLogger(h slog.Handler, namespace string) *slog.Logger { + return slog.New(h).With(slog.String(logging.NamespaceKey, namespace)) } diff --git a/go.mod b/go.mod index 3a8d362..aaeaa5b 100644 --- a/go.mod +++ b/go.mod @@ -9,48 +9,45 @@ require ( github.com/rhansen/go-kairos v0.0.0-20230622235007-87f08a46d19b github.com/stretchr/testify v1.12.1 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 - github.com/wavesplatform/gowaves v0.11.1 - go.uber.org/zap v1.28.0 + github.com/wavesplatform/gowaves v0.10.7-0.20260928125727-165e7a361c94 golang.org/x/sync v0.23.0 ) require ( filippo.io/edwards25519 v1.2.0 // indirect - github.com/bits-and-blooms/bitset v1.24.0 // indirect + github.com/bits-and-blooms/bitset v1.24.6 // indirect github.com/blang/semver/v4 v4.0.0 // indirect - github.com/btcsuite/btcd/btcec/v2 v2.3.6 // indirect - github.com/ccoveille/go-safecast/v2 v2.0.0 // indirect - github.com/cloudflare/circl v1.6.3 // indirect - github.com/consensys/gnark v0.14.0 // indirect - github.com/consensys/gnark-crypto v0.19.2 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.5.0 // indirect + github.com/ccoveille/go-safecast/v2 v2.0.1 // indirect + github.com/cloudflare/circl v1.6.5 // indirect + github.com/consensys/gnark v0.16.3 // indirect + github.com/consensys/gnark-crypto v0.21.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect github.com/dpotapov/slogpfx v0.0.0-20230917063348-41a73c95c536 // indirect github.com/golang/snappy v1.0.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 // indirect - github.com/ingonyama-zk/icicle-gnark/v3 v3.2.2 // indirect + github.com/google/pprof v0.0.0-20260802141513-ef3492d7dac3 // indirect github.com/jinzhu/copier v0.4.0 // indirect - github.com/lmittmann/tint v1.1.3 // indirect - github.com/mattn/go-colorable v0.1.14 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mr-tron/base58 v1.2.0 // indirect + github.com/lmittmann/tint v1.2.0 // indirect + github.com/mattn/go-colorable v0.1.15 // indirect + github.com/mattn/go-isatty v0.0.24 // indirect + github.com/mr-tron/base58 v1.3.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20250313105119-ba97887b0a25 // indirect github.com/ronanh/intcomp v1.1.1 // indirect - github.com/rs/zerolog v1.34.0 // indirect + github.com/rs/zerolog v1.35.1 // indirect github.com/stretchr/objx v0.5.3 // indirect github.com/umbracle/fastrlp v0.1.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/x448/float16 v0.8.4 // indirect - go.uber.org/atomic v1.11.0 // indirect - go.uber.org/multierr v1.11.0 // indirect + go.uber.org/atomic v1.12.0 // indirect go.yaml.in/yaml/v3 v3.0.5 // indirect - golang.org/x/crypto v0.49.0 // indirect - golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect - golang.org/x/net v0.51.0 // indirect - golang.org/x/sys v0.42.0 // indirect - golang.org/x/text v0.35.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect - google.golang.org/grpc v1.79.3 // indirect - google.golang.org/protobuf v1.36.11 // indirect + golang.org/x/crypto v0.57.0 // indirect + golang.org/x/exp v0.0.0-20260908205506-85c1c2202aba // indirect + golang.org/x/net v0.59.0 // indirect + golang.org/x/sys v0.48.0 // indirect + golang.org/x/text v0.42.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260921155816-b14227669459 // indirect + google.golang.org/grpc v1.84.0 // indirect + google.golang.org/protobuf v1.36.12 // indirect ) diff --git a/go.sum b/go.sum index b34d423..9fc4756 100644 --- a/go.sum +++ b/go.sum @@ -1,30 +1,27 @@ filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo= filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= -github.com/bits-and-blooms/bitset v1.24.0 h1:H4x4TuulnokZKvHLfzVRTHJfFfnHEeSYJizujEZvmAM= -github.com/bits-and-blooms/bitset v1.24.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bits-and-blooms/bitset v1.24.6 h1:qcrftZUVBIwfs+m+nhoCBAPT+ZPZZjti8SbHbDQQkZ4= +github.com/bits-and-blooms/bitset v1.24.6/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= -github.com/btcsuite/btcd/btcec/v2 v2.3.6 h1:IzlsEr9olcSRKB/n7c4351F3xHKxS2lma+1UFGCYd4E= -github.com/btcsuite/btcd/btcec/v2 v2.3.6/go.mod h1:m22FrOAiuxl/tht9wIqAoGHcbnCCaPWyauO8y2LGGtQ= -github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= -github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= -github.com/ccoveille/go-safecast/v2 v2.0.0 h1:+5eyITXAUj3wMjad6cRVJKGnC7vDS55zk0INzJagub0= -github.com/ccoveille/go-safecast/v2 v2.0.0/go.mod h1:JIYA4CAR33blIDuE6fSwCp2sz1oOBahXnvmdBhOAABs= -github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= -github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8= -github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= -github.com/consensys/gnark v0.14.0 h1:RG+8WxRanFSFBSlmCDRJnYMYYKpH3Ncs5SMzg24B5HQ= -github.com/consensys/gnark v0.14.0/go.mod h1:1IBpDPB/Rdyh55bQRR4b0z1WvfHQN1e0020jCvKP2Gk= -github.com/consensys/gnark-crypto v0.19.2 h1:qrEAIXq3T4egxqiliFFoNrepkIWVEeIYwt3UL0fvS80= -github.com/consensys/gnark-crypto v0.19.2/go.mod h1:rT23F0XSZqE0mUA0+pRtnL56IbPxs6gp4CeRsBk4XS0= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= -github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= +github.com/btcsuite/btcd/btcec/v2 v2.5.0 h1:KioMXOWa76b86sTZZOmbzv/ldaQCmB8KFAyn5PbB8E8= +github.com/btcsuite/btcd/btcec/v2 v2.5.0/go.mod h1:+K/MYXcLBtHEQjRbjHuJChuybk4LCgjdjgRwil+e+Kk= +github.com/btcsuite/btcd/chainhash/v2 v2.0.0 h1:PMLlSloHJuEeB80XG9EjpXWNEKAZAMLl6YHZ6YsEuoA= +github.com/btcsuite/btcd/chainhash/v2 v2.0.0/go.mod h1:mKxcZ7oGTXE7IRV+sS9hP4EVBwc/SzfNR+52IsOP9j8= +github.com/ccoveille/go-safecast/v2 v2.0.1 h1:2+mIu3gXtwmWelBia2kkxfB8eP4orTHDH7ClSlWkd6I= +github.com/ccoveille/go-safecast/v2 v2.0.1/go.mod h1:JIYA4CAR33blIDuE6fSwCp2sz1oOBahXnvmdBhOAABs= +github.com/cloudflare/circl v1.6.5 h1:O64F26HEqNhznd/hrC5KZXVKYuKM2rx4deZDTc4ihQA= +github.com/cloudflare/circl v1.6.5/go.mod h1:h5LNyxAc5nTue9DS5jT+48en2PSDYt3zdGnz5OstK6c= +github.com/consensys/gnark v0.16.3 h1:S7BtIQSX2WLHV2857HrLmrQ5xIl0ZRL8kT6rcLn8gow= +github.com/consensys/gnark v0.16.3/go.mod h1:ChMGCGi8KztMtuQXgxprorLVJY29FPnKkjN19RXB/KU= +github.com/consensys/gnark-crypto v0.21.0 h1:FDHibVIk4T5LkOKAkiN38g8gEvOxNcM10mLHOqvFTD0= +github.com/consensys/gnark-crypto v0.21.0/go.mod h1:hdTjDNjdkYJ1oVuc8emh9XEhfM1SbyZhJigFqItiOLk= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8= +github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= github.com/dpotapov/slogpfx v0.0.0-20230917063348-41a73c95c536 h1:3ZUyGIhpbUJVL3nwGRJO/DH1GRNb3qhKOteP1tMwFrA= github.com/dpotapov/slogpfx v0.0.0-20230917063348-41a73c95c536/go.mod h1:L9xGyDDA8E/83ucQSIKU/ZU3YfS3BzhyynT0ykxJGCk= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -34,11 +31,6 @@ github.com/fxamacker/cbor/v2 v2.9.4 h1:xwjVlxEMR3S605oUlgBjKLTTeGFciYPGYCtF/35LK github.com/fxamacker/cbor/v2 v2.9.4/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/go-chi/chi/v5 v5.3.2 h1:5YQkICvTCSZ25hoRsyJazN0scjzKGiu4VAUc7H1o1nY= github.com/go-chi/chi/v5 v5.3.2/go.mod h1:R+tYY2hNuVUUjxoPtqUdgBqevM9s9njzkTLutVsOCto= -github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= -github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= @@ -58,28 +50,21 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 h1:EEHtgt9IwisQ2AZ4pIsMjahcegHh6rmhqxzIRQIyepY= -github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/pprof v0.0.0-20260802141513-ef3492d7dac3 h1:LMLX+LgTNWpfvCBdFebv6EsYotImrt/Ppc5cXIriCSo= +github.com/google/pprof v0.0.0-20260802141513-ef3492d7dac3/go.mod h1:jl5iWTm0/hd5PjEYEOuwAJ57L/CibdZfrqZ5XA5GrCk= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/ingonyama-zk/icicle-gnark/v3 v3.2.2 h1:B+aWVgAx+GlFLhtYjIaF0uGjU3rzpl99Wf9wZWt+Mq8= -github.com/ingonyama-zk/icicle-gnark/v3 v3.2.2/go.mod h1:CH/cwcr21pPWH+9GtK/PFaa4OGTv4CtfkCKro6GpbRE= github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4= github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c= -github.com/lmittmann/tint v1.1.3 h1:Hv4EaHWXQr+GTFnOU4VKf8UvAtZgn0VuKT+G0wFlO3I= -github.com/lmittmann/tint v1.1.3/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= -github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -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/lmittmann/tint v1.2.0 h1:AogHRHy8HUJUnNJBHJlYa+fR4YY8mko2cnCp67xn9JY= +github.com/lmittmann/tint v1.2.0/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= +github.com/mattn/go-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy0/jY= +github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsReI= +github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A= +github.com/mr-tron/base58 v1.3.0 h1:K6Y13R2h+dku0wOqKtecgRnBUBPrZzLZy5aIj8lCcJI= +github.com/mr-tron/base58 v1.3.0/go.mod h1:2BuubE67DCSWwVfx37JWNG8emOC0sHEU4/HpcYgCLX8= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -99,9 +84,8 @@ github.com/rhansen/go-kairos v0.0.0-20230622235007-87f08a46d19b h1:FRUYsvFJXa6A7 github.com/rhansen/go-kairos v0.0.0-20230622235007-87f08a46d19b/go.mod h1:zpwvrKQ1hFbky5wnYOM1HoXwt9mmLqO3yx8JYgHlVfs= github.com/ronanh/intcomp v1.1.1 h1:+1bGV/wEBiHI0FvzS7RHgzqOpfbBJzLIxkqMJ9e6yxY= github.com/ronanh/intcomp v1.1.1/go.mod h1:7FOLy3P3Zj3er/kVrU/pl+Ql7JFZj7bwliMGketo0IU= -github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= -github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= -github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= +github.com/rs/zerolog v1.35.1 h1:m7xQeoiLIiV0BCEY4Hs+j2NG4Gp2o2KPKmhnnLiazKI= +github.com/rs/zerolog v1.35.1/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw= github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= @@ -112,44 +96,26 @@ github.com/umbracle/fastrlp v0.1.0 h1:V0W3f6ZKWqbu1KggdhnRWOi+t7+PfL3VyAffJqayI5 github.com/umbracle/fastrlp v0.1.0/go.mod h1:5RHgqiFjd4vLJESMWagP/E7su+5Gzk0iqqmrotR8WdA= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/wavesplatform/gowaves v0.11.1 h1:SdPl+sDKM0A4bxAwb5ycRE/fOUx77L5LBCCirBjom2g= -github.com/wavesplatform/gowaves v0.11.1/go.mod h1:9AKPZGLp9Wt3HzdkzbTpcIE8K8u32muOtvaeyxQieOw= +github.com/wavesplatform/gowaves v0.10.7-0.20260928125727-165e7a361c94 h1:uzipYCPzDBTUGsBIAdrDwoiU5DD9kwEeJq+8C1/33so= +github.com/wavesplatform/gowaves v0.10.7-0.20260928125727-165e7a361c94/go.mod h1:4ZcXbwHwzmz9ya7+Or6KQ6S+b8l4GEqC8ZAET5P9KrE= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= -go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= -go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= -go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= -go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= -go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= -go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= -go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= -go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= -go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= -go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= -go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= -go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= -go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= -go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= +go.uber.org/atomic v1.12.0 h1:BvcXdFKuviU4fTL/f+SxdQ5qJX/Jix8pAkgdUcb3XOE= +go.uber.org/atomic v1.12.0/go.mod h1:I6c4cg+6HCxRjfjSsYtApoFILnpc0CGUdGkXVqbYVNk= go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= 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.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= -golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= -golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0= -golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA= +golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M= +golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA= +golang.org/x/exp v0.0.0-20260908205506-85c1c2202aba h1:Ck8QetSgk912qxWLMCKxd0in+aiyBQyDSMae6e/xmpU= +golang.org/x/exp v0.0.0-20260908205506-85c1c2202aba/go.mod h1:50RgIsmK7OwqzTTeqcSXQW8SswW0o8fRcDxmqGluJ8E= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= -golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= +golang.org/x/net v0.59.0 h1:5zfYln+w5XCxwrnMMJPufRgNoXEaGxl0wo5GqPXyues= +golang.org/x/net v0.59.0/go.mod h1:2DA/G1UfVbCpQPeWTmMPGY7Cs2PkBkwu743bVX5PIVg= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.23.0 h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk= golang.org/x/sync v0.23.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0= @@ -162,34 +128,31 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= -golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo= +golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= -golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= +golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI= +golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= -google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE= -google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260921155816-b14227669459 h1:b0xCahf3FK2m2Cv0p4vTozGPWncCvLfwV86UNg8xWU8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260921155816-b14227669459/go.mod h1:OaIUM3+LpYcK2GXM4FTmhWoIq371Owdr+Cc7/BsYHHc= +google.golang.org/grpc v1.84.0 h1:soMyaPJ8pAak5PIQ0DGBUir0XRo2fRoMqhNWMLlLxO0= +google.golang.org/grpc v1.84.0/go.mod h1:ljCht0DrxQrXBDRTZp52Qxh3Ffk8CdYm2sj4O2QN2C0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= -google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc= +google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= diff --git a/listener.go b/listener.go index 377ff37..3b7a31b 100644 --- a/listener.go +++ b/listener.go @@ -2,11 +2,12 @@ package main import ( "context" + "log/slog" "net" - "go.uber.org/zap" "golang.org/x/sync/errgroup" + "github.com/wavesplatform/gowaves/pkg/logging" "github.com/wavesplatform/gowaves/pkg/proto" ) @@ -19,24 +20,27 @@ type Listener struct { cm *ConnectionManager nl net.Listener + + logger *slog.Logger } -func NewListener(bind, declared proto.TCPAddr, cm *ConnectionManager) Service { +func NewListener(bind, declared proto.TCPAddr, cm *ConnectionManager, logger *slog.Logger) Service { if declared.Empty() { - zap.S().Info("Declared address of Fork Detector is empty") - zap.S().Info("No network server will be started") + logger.Info("Declared address of Fork Detector is empty") + logger.Info("No network server will be started") return &EmptyService{} } if bind.Empty() && bind.Port == 0 { - zap.S().Warn("Bind address is empty") - zap.S().Warn("No network server will be started") + logger.Warn("Bind address is empty") + logger.Warn("No network server will be started") return &EmptyService{} } - zap.S().Infof("Starting network server on '%s'", bind.String()) + logger.Info("Starting network server", slog.String("bind", bind.String())) return &Listener{ bind: bind, declared: declared, cm: cm, + logger: logger, } } @@ -50,17 +54,17 @@ func (l *Listener) Run(ctx context.Context) { func (l *Listener) Shutdown() { if err := l.nl.Close(); err != nil { - zap.S().Errorf("Failed to close listener on %s: %v", l.bind, err) + l.logger.Error("Failed to close listener", slog.String("bind", l.bind.String()), logging.Error(err)) return } if err := l.wait(); err != nil { - zap.S().Warnf("Failed to shutdown Listener: %v", err) + l.logger.Warn("Failed to shutdown Listener", logging.Error(err)) } - zap.S().Info("Listener shutdown successfully") + l.logger.Info("Listener shutdown successfully") } func (l *Listener) run() error { - zap.S().Infof("Start listening on %s", l.bind.String()) + l.logger.Info("Start listening", slog.String("bind", l.bind.String())) var cfg net.ListenConfig nl, err := cfg.Listen(l.ctx, "tcp", l.bind.String()) if err != nil { @@ -75,13 +79,13 @@ func (l *Listener) run() error { default: conn, acErr := l.nl.Accept() if acErr != nil { - zap.S().Errorf("Failed to accept connection: %v", acErr) + l.logger.Error("Failed to accept connection", logging.Error(acErr)) continue } go func() { if aErr := l.cm.Accept(l.ctx, conn); aErr != nil { - zap.S().Debugf("[LSN] Failed to accept incoming connection from '%s': %v", - conn.RemoteAddr().String(), aErr) + l.logger.Debug("Failed to accept incoming connection", + slog.String("remote", conn.RemoteAddr().String()), logging.Error(aErr)) return } }() diff --git a/loading/loader.go b/loading/loader.go index a78a79c..ebe8782 100644 --- a/loading/loader.go +++ b/loading/loader.go @@ -2,14 +2,15 @@ package loading import ( "context" + "log/slog" "math/big" rand2 "math/rand/v2" "net/netip" "time" "github.com/rhansen/go-kairos/kairos" + "github.com/wavesplatform/gowaves/pkg/logging" "github.com/wavesplatform/gowaves/pkg/proto" - "go.uber.org/zap" "golang.org/x/sync/errgroup" "github.com/alexeykiselev/waves-fork-detector/chains" @@ -92,10 +93,13 @@ type Loader struct { timer *kairos.Timer ticker *kairos.Timer + + logger *slog.Logger } func NewLoader( registry *peers.Registry, linkage *chains.Linkage, idsCh <-chan IDsPackage, blockCh <-chan BlockPackage, + logger *slog.Logger, ) *Loader { return &Loader{ idsCh: idsCh, @@ -104,6 +108,7 @@ func NewLoader( linkage: linkage, timer: kairos.NewStoppedTimer(), ticker: kairos.NewStoppedTimer(), + logger: logger, } } @@ -118,10 +123,10 @@ func (l *Loader) Run(ctx context.Context) { func (l *Loader) Shutdown() { if err := l.wait(); err != nil { - zap.S().Warnf("Failed to shutdown Loader: %v", err) + l.logger.Warn("Failed to shutdown Loader", logging.Error(err)) } l.ticker.Stop() - zap.S().Info("Loader shutdown successfully") + l.logger.Info("Loader shutdown successfully") } func (l *Loader) OK() { @@ -130,20 +135,20 @@ func (l *Loader) OK() { // Check score again and continue sync if needed. p, err := l.registry.Peer(l.pl.peer.ID()) if err != nil { - zap.S().Warnf("Failed to get connection: %v", err) + l.logger.Warn("Failed to get connection", logging.Error(err)) return } score, err := l.linkage.LeashScore(l.pl.peer.ID()) if err != nil { - zap.S().Warnf("Failed to get peers score: %v", err) + l.logger.Warn("Failed to get peers score", logging.Error(err)) return } if diff, lagging := l.isLagging(p.Score, score); lagging { - zap.S().Debugf("[LDR] Peer '%s' is still lagging, continue loading", l.pl.peer.ID()) + l.logger.Debug("Peer is still lagging, continue loading", slog.String("peer", l.pl.peer.ID().String())) l.prevDiff = diff l.continueSync() } else { - zap.S().Debugf("[LDR] Peer '%s' is not lagging anymore, resetting loading peer", l.pl.peer.ID()) + l.logger.Debug("Peer is not lagging anymore, resetting loading peer", slog.String("peer", l.pl.peer.ID().String())) l.resetLoadingPeer() } } @@ -151,7 +156,7 @@ func (l *Loader) OK() { func (l *Loader) Fail() { if l.pl != nil && l.pl.sm.MustState() == stateDone { - zap.S().Debugf("[LDR] Peer '%s' failed to load history", l.pl.peer.ID()) + l.logger.Debug("Peer failed to load history", slog.String("peer", l.pl.peer.ID().String())) l.fc.inc(l.pl.peer.ID()) l.pl = nil } @@ -190,23 +195,23 @@ func (l *Loader) sync() { // Get lagging connected peers. connections, err := l.registry.Connections() if err != nil { - zap.S().Warnf("Failed to get connections: %v", err) + l.logger.Warn("Failed to get connections", logging.Error(err)) return // Failed to get connections, try again later. } if len(connections) == 0 { - zap.S().Debug("[LDR] No connected peers") + l.logger.Debug("No connected peers") return // No connections, try again later. } - zap.S().Debugf("[LDR] Trying to sync with %d connections", len(connections)) + l.logger.Debug("Trying to sync with connections", slog.Int("count", len(connections))) lagging := make([]peers.Peer, 0, len(connections)) for _, cp := range connections { if cp.Score == nil { - zap.S().Debugf("[LDR] Peer '%s' has no score", cp.AddressPort.Addr().String()) + l.logger.Debug("Peer has no score", slog.String("peer", cp.AddressPort.Addr().String())) continue // For this peer broadcast score is unknown, skip it. } score, lsErr := l.linkage.LeashScore(cp.AddressPort.Addr()) if lsErr != nil { - zap.S().Warnf("Failed to get peers score: %v", lsErr) + l.logger.Warn("Failed to get peers score", logging.Error(lsErr)) continue } // Peer's broadcast score is greater than peer's leash score that means we have to restore history chain @@ -216,14 +221,14 @@ func (l *Loader) sync() { } } if len(lagging) == 0 { - zap.S().Infof("No lagging peers") + l.logger.Info("No lagging peers") return } - zap.S().Infof("Syncing with one of %d lagging peers", len(lagging)) + l.logger.Info("Syncing with one of lagging peers", slog.Int("count", len(lagging))) // Select random lagging peer and start synchronization with it. p := lagging[rand2.IntN(len(lagging))] //nolint:gosec //we don't need a crypto rand here. - l.pl = newPeerLoader(&p, l.linkage, l) - zap.S().Infof("Start loading history for peer '%s'", p.ID()) + l.pl = newPeerLoader(&p, l.linkage, l, l.logger.With(slog.String("peer", p.ID().String()))) + l.logger.Info("Start loading history for peer", slog.String("peer", p.ID().String())) if l.fc.count(l.pl.peer.ID()) >= restartThreshold { l.restartSync() } @@ -233,16 +238,17 @@ func (l *Loader) sync() { func (l *Loader) continueSync() { l.ticker.Reset(tickerInterval) // Start ticker. if err := l.pl.start(); err != nil { - zap.S().Warnf("Failed to syncronize with peer '%s': %v", l.pl.peer.ID(), err) + l.logger.Warn("Failed to synchronize with peer", slog.String("peer", l.pl.peer.ID().String()), logging.Error(err)) } } func (l *Loader) restartSync() { - zap.S().Debugf("[LDR] Restarting syncronization with peer '%s'", l.pl.peer.ID()) + l.logger.Debug("Restarting synchronization with peer", slog.String("peer", l.pl.peer.ID().String())) l.fc.reset(l.pl.peer.ID()) l.ticker.Reset(tickerInterval) if err := l.pl.restart(); err != nil { - zap.S().Warnf("Failed to restart syncronization with peer '%s': %v", l.pl.peer.ID(), err) + l.logger.Warn("Failed to restart synchronization with peer", slog.String("peer", l.pl.peer.ID().String()), + logging.Error(err)) } } @@ -251,7 +257,7 @@ func (l *Loader) tick() { return } if err := l.pl.processTick(time.Now()); err != nil { - zap.S().Warnf("Failed to process tick on peer '%s': %v", l.pl.peer.ID(), err) + l.logger.Warn("Failed to process tick on peer", slog.String("peer", l.pl.peer.ID().String()), logging.Error(err)) } } @@ -266,7 +272,7 @@ func (l *Loader) handleIDs(p IDsPackage) { return // Ignore IDs from unexpected peers. } if err := l.pl.processIDs(p.IDs); err != nil { - zap.S().Warnf("Failed to process IDs for peer '%s': %v", p.Peer.String(), err) + l.logger.Warn("Failed to process IDs for peer", slog.String("peer", p.Peer.String()), logging.Error(err)) } } @@ -278,7 +284,7 @@ func (l *Loader) handleBlock(p BlockPackage) { return // Ignore blocks from unexpected peers. } if err := l.pl.processBlock(p.Block); err != nil { - zap.S().Warnf("Failed to process block for peer '%s': %v", p.Peer.String(), err) + l.logger.Warn("Failed to process block for peer", slog.String("peer", p.Peer.String()), logging.Error(err)) } } diff --git a/loading/peer_loader.go b/loading/peer_loader.go index febfa25..02f98dd 100644 --- a/loading/peer_loader.go +++ b/loading/peer_loader.go @@ -3,12 +3,12 @@ package loading import ( "context" "fmt" + "log/slog" "reflect" "time" "github.com/qmuntal/stateless" "github.com/wavesplatform/gowaves/pkg/proto" - "go.uber.org/zap" "github.com/alexeykiselev/waves-fork-detector/chains" "github.com/alexeykiselev/waves-fork-detector/peers" @@ -28,14 +28,19 @@ type peerLoader struct { timestamp time.Time // Time then operation was started. Used to calculate timeout. queue queue + + logger *slog.Logger // Loader's logger with the peer attribute. } -func newPeerLoader(peer peers.HistoryRequester, hp chains.HistoryProvider, r Reporter) *peerLoader { +func newPeerLoader( + peer peers.HistoryRequester, hp chains.HistoryProvider, r Reporter, logger *slog.Logger, +) *peerLoader { pl := &peerLoader{ - sm: stateless.NewStateMachine(stateIdle), - peer: peer, - hp: hp, - r: r, + sm: stateless.NewStateMachine(stateIdle), + peer: peer, + hp: hp, + r: r, + logger: logger, } pl.sm.SetTriggerParameters(eventIDs, reflect.TypeOf([]proto.BlockID{})) pl.sm.SetTriggerParameters(eventBlock, reflect.TypeOf((*proto.Block)(nil))) @@ -143,7 +148,7 @@ func (pl *peerLoader) onTick(_ context.Context, args ...any) error { return fmt.Errorf("failed to process tick for peer '%s': invalid argument type", pl.peer.ID()) } if d := tm.Sub(pl.timestamp); d > timeoutDuration { - zap.S().Debugf("[PL@%s] Timeout (%s) in state %s", pl.peer.ID(), d, pl.sm.MustState()) + pl.logger.Debug("Timeout", slog.Duration("duration", d), slog.Any("state", pl.sm.MustState())) return pl.sm.Fire(eventTimeout) } return nil @@ -209,7 +214,7 @@ func (pl *peerLoader) requestBlocks(_ context.Context, args ...any) error { } pl.queue = newQueue(req) - zap.S().Infof("Requesting blocks %s from peer '%s'", pl.queue.rangeString(), pl.peer.ID()) + pl.logger.Info("Requesting blocks from peer", slog.String("range", pl.queue.rangeString())) for _, id := range req { pl.peer.RequestBlock(id) } @@ -230,7 +235,7 @@ func (pl *peerLoader) appendBlock(_ context.Context, args ...any) error { it.received.BlockID(), pl.peer.ID(), err) } } - zap.S().Debugf("[PL@%s] Blocks %s loaded", pl.peer.ID(), pl.queue.rangeString()) + pl.logger.Debug("Blocks loaded", slog.String("range", pl.queue.rangeString())) return pl.sm.Fire(eventQueueReady) } return nil diff --git a/loading/peer_loader_internal_test.go b/loading/peer_loader_internal_test.go index 6054dd8..2cfaa2d 100644 --- a/loading/peer_loader_internal_test.go +++ b/loading/peer_loader_internal_test.go @@ -1,6 +1,7 @@ package loading import ( + "log/slog" "net/netip" "testing" "time" @@ -18,7 +19,7 @@ import ( func TestPeerLoaderSimpleOneIDsBatch(t *testing.T) { addr := netip.MustParseAddr("8.8.8.8") - bl1 := createNthBlock(t, settings.TestNetSettings.Genesis.BlockID(), 1) + bl1 := createNthBlock(t, settings.MustTestNetSettings().Genesis.BlockID(), 1) bl2 := createNthBlock(t, bl1.BlockID(), 2) bl3 := createNthBlock(t, bl2.BlockID(), 3) @@ -40,7 +41,7 @@ func TestPeerLoaderSimpleOneIDsBatch(t *testing.T) { mr := mockloading.NewMockReporter(t) mr.On("OK").Once() - pl := newPeerLoader(mhr, mhp, mr) + pl := newPeerLoader(mhr, mhp, mr, slog.New(slog.DiscardHandler)) assert.Equal(t, stateIdle, pl.sm.MustState()) err := pl.start() require.NoError(t, err) @@ -76,7 +77,7 @@ func TestTimeoutOnWaitingForIDs(t *testing.T) { mr := mockloading.NewMockReporter(t) mr.On("Fail").Once() - pl := newPeerLoader(mhr, mhp, mr) + pl := newPeerLoader(mhr, mhp, mr, slog.New(slog.DiscardHandler)) assert.Equal(t, stateIdle, pl.sm.MustState()) err := pl.start() require.NoError(t, err) @@ -93,7 +94,7 @@ func TestTimeoutOnWaitingForBlocks(t *testing.T) { addr := netip.MustParseAddr("8.8.8.8") start := time.Now() - bl1 := createNthBlock(t, settings.TestNetSettings.Genesis.BlockID(), 1) + bl1 := createNthBlock(t, settings.MustTestNetSettings().Genesis.BlockID(), 1) bl2 := createNthBlock(t, bl1.BlockID(), 2) bl3 := createNthBlock(t, bl2.BlockID(), 3) @@ -115,7 +116,7 @@ func TestTimeoutOnWaitingForBlocks(t *testing.T) { mr := mockloading.NewMockReporter(t) mr.On("Fail").Once() - pl := newPeerLoader(mhr, mhp, mr) + pl := newPeerLoader(mhr, mhp, mr, slog.New(slog.DiscardHandler)) assert.Equal(t, stateIdle, pl.sm.MustState()) err := pl.start() require.NoError(t, err) diff --git a/loading/queue_internal_test.go b/loading/queue_internal_test.go index ac9fefa..e82d150 100644 --- a/loading/queue_internal_test.go +++ b/loading/queue_internal_test.go @@ -12,7 +12,7 @@ import ( ) func TestNewQueue(t *testing.T) { - bl1 := createNthBlock(t, settings.TestNetSettings.Genesis.BlockID(), 1) + bl1 := createNthBlock(t, settings.MustTestNetSettings().Genesis.BlockID(), 1) bl2 := createNthBlock(t, bl1.BlockID(), 2) bl3 := createNthBlock(t, bl2.BlockID(), 3) ids := []proto.BlockID{bl1.BlockID(), bl2.BlockID(), bl3.BlockID()} @@ -24,7 +24,7 @@ func TestNewQueue(t *testing.T) { } func TestQueuePutAndLastReceived(t *testing.T) { - bl1 := createNthBlock(t, settings.TestNetSettings.Genesis.BlockID(), 1) + bl1 := createNthBlock(t, settings.MustTestNetSettings().Genesis.BlockID(), 1) bl2 := createNthBlock(t, bl1.BlockID(), 2) bl3 := createNthBlock(t, bl2.BlockID(), 3) fake := createNthBlock(t, bl3.BlockID(), 4) @@ -61,7 +61,7 @@ func TestQueuePutAndLastReceived(t *testing.T) { } func TestReady(t *testing.T) { - bl1 := createNthBlock(t, settings.TestNetSettings.Genesis.BlockID(), 1) + bl1 := createNthBlock(t, settings.MustTestNetSettings().Genesis.BlockID(), 1) bl2 := createNthBlock(t, bl1.BlockID(), 2) bl3 := createNthBlock(t, bl2.BlockID(), 3) fake := createNthBlock(t, bl3.BlockID(), 4) @@ -79,7 +79,7 @@ func TestReady(t *testing.T) { } func TestRangeString(t *testing.T) { - bl1 := createNthBlock(t, settings.TestNetSettings.Genesis.BlockID(), 1) + bl1 := createNthBlock(t, settings.MustTestNetSettings().Genesis.BlockID(), 1) bl2 := createNthBlock(t, bl1.BlockID(), 2) bl3 := createNthBlock(t, bl2.BlockID(), 3) ids := []proto.BlockID{bl1.BlockID(), bl2.BlockID(), bl3.BlockID()} diff --git a/parameters.go b/parameters.go index 0bb480c..904f581 100644 --- a/parameters.go +++ b/parameters.go @@ -4,6 +4,7 @@ import ( "errors" "flag" "fmt" + "log/slog" "math" rand2 "math/rand/v2" "net" @@ -12,15 +13,15 @@ import ( "path" "strings" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" - + "github.com/wavesplatform/gowaves/pkg/logging" "github.com/wavesplatform/gowaves/pkg/proto" "github.com/wavesplatform/gowaves/pkg/settings" ) type parameters struct { - logLevel zapcore.Level + lp logging.Parameters + logNetwork bool + logNetworkData bool dbPath string scheme proto.Scheme genesis proto.Block @@ -34,8 +35,12 @@ type parameters struct { } func newParameters() (*parameters, error) { - ll := zap.LevelFlag("log-level", zapcore.InfoLevel, - "Specify the logging level. Supported levels include: DEBUG, INFO, WARN, ERROR, and FATAL.") + lp := logging.Parameters{} + lp.Initialize() + flagLogNetwork := flag.Bool("log-network", false, + "Log the operation of network stack. Turned off by default.") + flagLogNetworkData := flag.Bool("log-network-data", false, + "Log network messages as Base64 strings. Turned off by default.") flagDB := flag.String("db", "", "Specify the path to the database folder. There is no default value. Please, provide one.") flagBlockchainType := flag.String("blockchain-type", "mainnet", @@ -55,6 +60,9 @@ func newParameters() (*parameters, error) { "Specify a comma-separated list of acceptable protocol versions. "+ "By default, the two most recent versions are accepted.") flag.Parse() + if err := lp.Parse(); err != nil { + return nil, fmt.Errorf("invalid parameters: %w", err) + } dbf, err := checkFolder(*flagDB) if err != nil { return nil, fmt.Errorf("invalid parameters: invalid database folder: %w", err) @@ -84,7 +92,9 @@ func newParameters() (*parameters, error) { return nil, fmt.Errorf("invalid parameters: %w", err) } return ¶meters{ - logLevel: *ll, + lp: lp, + logNetwork: *flagLogNetwork, + logNetworkData: *flagLogNetworkData, dbPath: dbf, scheme: bs.AddressSchemeCharacter, genesis: bs.Genesis, @@ -99,17 +109,20 @@ func newParameters() (*parameters, error) { } func (p *parameters) log() { - zap.S().Debugf("Configuration parameters:") - zap.S().Debugf("\tlogLevel: %s", p.logLevel) - zap.S().Debugf("\tdbPath: %s", p.dbPath) - zap.S().Debugf("\tscheme: %c", p.scheme) - zap.S().Debugf("\tgenesis: %s", p.genesis.BlockID().String()) - zap.S().Debugf("\tseedPeers: %v", p.seedPeers) - zap.S().Debugf("\tapiBind: %s", p.apiBind) - zap.S().Debugf("\tnetBind: %s", p.netBind) - zap.S().Debugf("\tdeclaredAddress: %s", p.declaredAddress) - zap.S().Debugf("\tname: %s", p.name) - zap.S().Debugf("\tversions: %s", p.versions) + slog.Debug("Configuration parameters", + slog.String("logging", p.lp.String()), + slog.Bool("logNetwork", p.logNetwork), + slog.Bool("logNetworkData", p.logNetworkData), + slog.String("dbPath", p.dbPath), + slog.String("scheme", string(rune(p.scheme))), + slog.String("genesis", p.genesis.BlockID().String()), + slog.Any("seedPeers", p.seedPeers), + slog.String("apiBind", p.apiBind), + slog.String("netBind", p.netBind.String()), + slog.String("declaredAddress", p.declaredAddress.String()), + slog.String("name", p.name), + slog.Any("versions", p.versions), + ) } func parseSeedPeers(seedPeers string, blockchainType settings.BlockchainType) ([]*net.TCPAddr, error) { diff --git a/peers/peer.go b/peers/peer.go index 9790e0a..f628969 100644 --- a/peers/peer.go +++ b/peers/peer.go @@ -2,13 +2,13 @@ package peers import ( "fmt" + "log/slog" "math/big" "net" "net/netip" "time" "github.com/wavesplatform/gowaves/pkg/crypto" - "go.uber.org/zap" "github.com/wavesplatform/gowaves/pkg/p2p/peer" "github.com/wavesplatform/gowaves/pkg/proto" @@ -29,6 +29,7 @@ type Peer struct { NextAttempt time.Time `json:"next_attempt"` Score *big.Int `json:"score"` p peer.Peer + logger *slog.Logger } func (p *Peer) String() string { @@ -52,9 +53,17 @@ func (p *Peer) TCPAddr() *net.TCPAddr { } } +// log returns the peer's logger, or a discarding logger if the peer has no active connection. +func (p *Peer) log() *slog.Logger { + if p.logger == nil { + return slog.New(slog.DiscardHandler) + } + return p.logger +} + func (p *Peer) Send(msg proto.Message) { if p.p != nil { - zap.S().Debugf("Sending message to %s", p.AddressPort.String()) + p.log().Debug("Sending message") p.p.SendMessage(msg) } } @@ -66,12 +75,12 @@ func (p *Peer) RequestBlockIDs(ids []proto.BlockID) { for i, id := range ids { sigs[i] = id.Signature() } - zap.S().Debugf("[%s] Requesting signatures for signatures range [%s...%s]", - p.p.ID().String(), sigs[0].ShortString(), sigs[len(sigs)-1].ShortString()) + p.log().Debug("Requesting signatures for signatures range", + slog.String("first", sigs[0].ShortString()), slog.String("last", sigs[len(sigs)-1].ShortString())) p.p.SendMessage(&proto.GetSignaturesMessage{Signatures: sigs}) } else { - zap.S().Debugf("[%s] Requesting blocks IDs for IDs range [%s...%s]", - p.p.ID().String(), ids[0].ShortString(), ids[len(ids)-1].ShortString()) + p.log().Debug("Requesting blocks IDs for IDs range", + slog.String("first", ids[0].ShortString()), slog.String("last", ids[len(ids)-1].ShortString())) p.p.SendMessage(&proto.GetBlockIDsMessage{Blocks: ids}) } } diff --git a/peers/registry.go b/peers/registry.go index e7f0b39..503b301 100644 --- a/peers/registry.go +++ b/peers/registry.go @@ -3,6 +3,7 @@ package peers import ( "errors" "fmt" + "log/slog" "math/big" "net" "net/netip" @@ -11,8 +12,8 @@ import ( "time" "github.com/syndtr/goleveldb/leveldb" - "go.uber.org/zap" + "github.com/wavesplatform/gowaves/pkg/logging" "github.com/wavesplatform/gowaves/pkg/p2p/peer" "github.com/wavesplatform/gowaves/pkg/proto" ) @@ -30,10 +31,12 @@ type Registry struct { mu sync.Mutex connections map[netip.Addr]peer.Peer pending map[netip.Addr]struct{} + + logger *slog.Logger } func NewRegistry( - scheme proto.Scheme, declared proto.TCPAddr, versions []proto.Version, path string, + scheme proto.Scheme, declared proto.TCPAddr, versions []proto.Version, path string, logger *slog.Logger, ) (*Registry, error) { s, err := newStorage(path) if err != nil { @@ -53,6 +56,7 @@ func NewRegistry( storage: s, connections: make(map[netip.Addr]peer.Peer), pending: make(map[netip.Addr]struct{}), + logger: logger, }, nil } @@ -104,7 +108,7 @@ func (r *Registry) RegisterPeer(addr netip.Addr, np peer.Peer, handshake proto.H return r.storage.putPeer(p) } - port, err := checkPort(addr, np, handshake) + port, err := r.checkPort(addr, np, handshake) if err != nil { return err } @@ -133,7 +137,7 @@ func (r *Registry) UpdatePeerScore(addr netip.Addr, score *big.Int) error { return r.storage.putPeer(p) } -func checkPort(addr netip.Addr, np peer.Peer, handshake proto.Handshake) (uint16, error) { +func (r *Registry) checkPort(addr netip.Addr, np peer.Peer, handshake proto.Handshake) (uint16, error) { port := uint16(0) if !handshake.DeclaredAddr.Empty() { ha, pErr := netip.ParseAddrPort(handshake.DeclaredAddr.String()) @@ -150,7 +154,8 @@ func checkPort(addr netip.Addr, np peer.Peer, handshake proto.Handshake) (uint16 return 0, fmt.Errorf("failed to register peer: declared address is not IPv4: '%s'", ha.String()) } if ha.Addr().Compare(addr) != 0 { - zap.S().Warnf("Declared address '%s' does not match actual remote address '%s'", ha.String(), addr.String()) + r.logger.Warn("Declared address does not match actual remote address", slog.String("declared", ha.String()), + slog.String("remote", addr.String())) port = 0 } } @@ -164,7 +169,7 @@ func (r *Registry) UnregisterPeer(addr netip.Addr) error { p, err := r.storage.peer(addr) if err != nil { if errors.Is(err, leveldb.ErrNotFound) { - zap.S().Warnf("Attempt to unregister unknown peer '%s'", addr.String()) + r.logger.Warn("Attempt to unregister unknown peer", slog.String("peer", addr.String())) } return fmt.Errorf("failed to unregister peer '%s': %w", addr.String(), err) } @@ -214,6 +219,7 @@ func (r *Registry) Connections() ([]Peer, error) { return nil, fmt.Errorf("failed to get active connetcions: %w", err) } sp.p = p + sp.logger = r.logger.With(slog.String("peer", a.String())) connections[i] = sp i++ } @@ -231,24 +237,24 @@ func (r *Registry) AppendAddresses(addresses []*net.TCPAddr) int { for i := range addresses { ap, err := netip.ParseAddrPort(addresses[i].String()) if err != nil { - zap.S().Debugf("Error adding address: %v", err) + r.logger.Debug("Error adding address", logging.Error(err)) continue } if !ap.Addr().Is4() { - zap.S().Debugf("[REG] Skipping non-IPv4 address: %s", ap.String()) + r.logger.Debug("Skipping non-IPv4 address", slog.String("address", ap.String())) continue } if ap.Addr().IsLoopback() { - zap.S().Debugf("[REG] Skipping loopback address: %s", ap.String()) + r.logger.Debug("Skipping loopback address", slog.String("address", ap.String())) continue } if ap.Addr().Compare(r.declared) == 0 { - zap.S().Debugf("[REG] Skipping self address: %s", ap.String()) + r.logger.Debug("Skipping self address", slog.String("address", ap.String())) continue } yes, err := r.storage.hasPeer(ap.Addr()) if err != nil { - zap.S().Debugf("[REG] Failed to append addresses: %v", err) + r.logger.Debug("Failed to append addresses", logging.Error(err)) return count } if !yes { @@ -257,7 +263,7 @@ func (r *Registry) AppendAddresses(addresses []*net.TCPAddr) int { State: PeerUnknown, } if putErr := r.storage.putPeer(p); putErr != nil { - zap.S().Warnf("Failed to append addresses: %v", putErr) + r.logger.Warn("Failed to append addresses", logging.Error(putErr)) return count } count++ @@ -326,7 +332,8 @@ func (r *Registry) TakeAvailableAddresses() ([]netip.AddrPort, error) { if err != nil { return addresses, fmt.Errorf("failed to get available addresses from storage: %w", err) } - zap.S().Debugf("[REG] Getting available addresses: pending %d, connected %d", len(r.pending), len(r.connections)) + r.logger.Debug("Getting available addresses", slog.Int("pending", len(r.pending)), + slog.Int("connected", len(r.connections))) for _, p := range peers { if p.State == PeerHostile { continue diff --git a/respawn.go b/respawn.go index 0a9aaef..30ac8b5 100644 --- a/respawn.go +++ b/respawn.go @@ -2,12 +2,13 @@ package main import ( "context" + "log/slog" "net/netip" "time" - "go.uber.org/zap" "golang.org/x/sync/errgroup" + "github.com/wavesplatform/gowaves/pkg/logging" "github.com/wavesplatform/gowaves/pkg/proto" "github.com/rhansen/go-kairos/kairos" @@ -22,15 +23,17 @@ type Respawn struct { wait func() error timer *kairos.Timer - reg *peers.Registry - cm *ConnectionManager + reg *peers.Registry + cm *ConnectionManager + logger *slog.Logger } -func NewRespawn(reg *peers.Registry, cm *ConnectionManager) *Respawn { +func NewRespawn(reg *peers.Registry, cm *ConnectionManager, logger *slog.Logger) *Respawn { return &Respawn{ - timer: kairos.NewStoppedTimer(), - reg: reg, - cm: cm, + timer: kairos.NewStoppedTimer(), + reg: reg, + cm: cm, + logger: logger, } } @@ -45,9 +48,9 @@ func (r *Respawn) Run(ctx context.Context) { func (r *Respawn) Shutdown() { if err := r.wait(); err != nil { - zap.S().Warnf("Failed to shutdown Respawn: %v", err) + r.logger.Warn("Failed to shutdown Respawn", logging.Error(err)) } - zap.S().Info("Respawn shutdown successfully") + r.logger.Info("Respawn shutdown successfully") } func (r *Respawn) handleEvents() error { @@ -58,13 +61,13 @@ func (r *Respawn) handleEvents() error { case <-r.timer.C: addresses, err := r.reg.TakeAvailableAddresses() if len(addresses) > 0 { - zap.S().Infof("Trying to establish connections to %d available addresses", len(addresses)) + r.logger.Info("Trying to establish connections to available addresses", slog.Int("count", len(addresses))) } else { - zap.S().Debugf("[RSP] No available addresses to establish connections") + r.logger.Debug("No available addresses to establish connections") } if err != nil { - zap.S().Warnf("Failed to take available addresses: %v", err) + r.logger.Warn("Failed to take available addresses", logging.Error(err)) continue } r.establishConnections(addresses) @@ -78,9 +81,10 @@ func (r *Respawn) establishConnections(addresses []netip.AddrPort) { go func(ap netip.AddrPort) { addr := proto.NewTCPAddrFromString(ap.String()) if cErr := r.cm.Connect(r.ctx, addr); cErr != nil { - zap.S().Debugf("[RSP] Failed to establish outbound connection: %v", cErr) + r.logger.Debug("Failed to establish outbound connection", slog.String("address", ap.String()), logging.Error(cErr)) if urErr := r.reg.UnregisterPeer(ap.Addr()); urErr != nil { - zap.S().Warnf("Failed to unregister peer on connection failure: %v", urErr) + r.logger.Warn("Failed to unregister peer on connection failure", slog.String("address", ap.String()), + logging.Error(urErr)) return } } From b5e03b9bed0da441b5a0ace385cb66c2e33f153d Mon Sep 17 00:00:00 2001 From: Alexey Kiselev Date: Thu, 1 Oct 2026 11:14:19 +0400 Subject: [PATCH 3/9] Fix error propagation. --- api/api.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/api/api.go b/api/api.go index f7487ce..893d2c5 100644 --- a/api/api.go +++ b/api/api.go @@ -11,7 +11,6 @@ import ( "log/slog" "net/http" "net/netip" - "os" "runtime" "sort" "strconv" @@ -116,7 +115,7 @@ func (a *API) runServer() error { err := a.srv.ListenAndServe() if err != nil && !errors.Is(err, http.ErrServerClosed) { a.logger.Error("Failed to start API", logging.Error(err)) - os.Exit(1) + return err } return nil } From 91b0975d8aaa189e942b02bb9e056e39cacc29bc Mon Sep 17 00:00:00 2001 From: Alexey Kiselev Date: Thu, 1 Oct 2026 12:40:25 +0400 Subject: [PATCH 4/9] Some code simplifications. --- chains/linkage_internal_test.go | 14 ++++++-------- loading/queue_internal_test.go | 14 ++++++-------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/chains/linkage_internal_test.go b/chains/linkage_internal_test.go index 754d951..14c0298 100644 --- a/chains/linkage_internal_test.go +++ b/chains/linkage_internal_test.go @@ -276,13 +276,11 @@ func createNthBlock(t testing.TB, prev proto.BlockID, n int) *proto.Block { const tsStep uint64 = 10000 id := testID(t, fmt.Sprintf("BLOCK%d", n)) return &proto.Block{ - BlockHeader: proto.BlockHeader{ - Version: proto.ProtobufBlockVersion, - Timestamp: tsStep * uint64(n), - Parent: prev, - NxtConsensus: proto.NxtConsensus{BaseTarget: 12345}, - GeneratorPublicKey: crypto.PublicKey{}, - ID: id, - }, + Version: proto.ProtobufBlockVersion, + Timestamp: tsStep * uint64(n), + Parent: prev, + BaseTarget: 12345, + GeneratorPublicKey: crypto.PublicKey{}, + ID: id, } } diff --git a/loading/queue_internal_test.go b/loading/queue_internal_test.go index e82d150..f5c4642 100644 --- a/loading/queue_internal_test.go +++ b/loading/queue_internal_test.go @@ -105,13 +105,11 @@ func createNthBlock(t testing.TB, prev proto.BlockID, n int) *proto.Block { const tsStep uint64 = 10000 id := testID(t, fmt.Sprintf("BLOCK%d", n)) return &proto.Block{ - BlockHeader: proto.BlockHeader{ - Version: proto.ProtobufBlockVersion, - Timestamp: tsStep * uint64(n), - Parent: prev, - NxtConsensus: proto.NxtConsensus{BaseTarget: 12345}, - GeneratorPublicKey: crypto.PublicKey{}, - ID: id, - }, + Version: proto.ProtobufBlockVersion, + Timestamp: tsStep * uint64(n), + Parent: prev, + BaseTarget: 12345, + GeneratorPublicKey: crypto.PublicKey{}, + ID: id, } } From e5416f53681b194c66c22596d194bdefa38e5215 Mon Sep 17 00:00:00 2001 From: Alexey Kiselev Date: Thu, 1 Oct 2026 17:05:22 +0400 Subject: [PATCH 5/9] Update gowaves to the latest master to fix network data logging. --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index aaeaa5b..b143a9c 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/rhansen/go-kairos v0.0.0-20230622235007-87f08a46d19b github.com/stretchr/testify v1.12.1 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 - github.com/wavesplatform/gowaves v0.10.7-0.20260928125727-165e7a361c94 + github.com/wavesplatform/gowaves v0.10.7-0.20261001122251-fc8acb090fce golang.org/x/sync v0.23.0 ) diff --git a/go.sum b/go.sum index 9fc4756..4e056a7 100644 --- a/go.sum +++ b/go.sum @@ -96,8 +96,8 @@ github.com/umbracle/fastrlp v0.1.0 h1:V0W3f6ZKWqbu1KggdhnRWOi+t7+PfL3VyAffJqayI5 github.com/umbracle/fastrlp v0.1.0/go.mod h1:5RHgqiFjd4vLJESMWagP/E7su+5Gzk0iqqmrotR8WdA= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/wavesplatform/gowaves v0.10.7-0.20260928125727-165e7a361c94 h1:uzipYCPzDBTUGsBIAdrDwoiU5DD9kwEeJq+8C1/33so= -github.com/wavesplatform/gowaves v0.10.7-0.20260928125727-165e7a361c94/go.mod h1:4ZcXbwHwzmz9ya7+Or6KQ6S+b8l4GEqC8ZAET5P9KrE= +github.com/wavesplatform/gowaves v0.10.7-0.20261001122251-fc8acb090fce h1:JHBGxTn0Y9WgBMq4wq9RgSkmwXk29DmeVIX7WZxRjH8= +github.com/wavesplatform/gowaves v0.10.7-0.20261001122251-fc8acb090fce/go.mod h1:4ZcXbwHwzmz9ya7+Or6KQ6S+b8l4GEqC8ZAET5P9KrE= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= go.uber.org/atomic v1.12.0 h1:BvcXdFKuviU4fTL/f+SxdQ5qJX/Jix8pAkgdUcb3XOE= From 591345599bc60d0268219539cf8742838b52a1a7 Mon Sep 17 00:00:00 2001 From: Alexey Kiselev Date: Thu, 1 Oct 2026 17:54:15 +0400 Subject: [PATCH 6/9] Improved peer management implemented. Hard limit on peers message size added. --- distributor.go | 18 +++-- forkdetector.go | 19 ++++-- forkdetector_internal_test.go | 64 +++++++++++++++++ peers/peer.go | 1 + peers/registry.go | 113 ++++++++++++++++++++++++++++-- peers/registry_internal_test.go | 117 ++++++++++++++++++++++++++++++++ peers/storage.go | 15 +++- peers/storage_internal_test.go | 1 + peers/version_provider.go | 1 + respawn.go | 20 +++++- 10 files changed, 352 insertions(+), 17 deletions(-) create mode 100644 forkdetector_internal_test.go diff --git a/distributor.go b/distributor.go index 2b155dd..e405e29 100644 --- a/distributor.go +++ b/distributor.go @@ -6,6 +6,7 @@ import ( "fmt" "log/slog" "math/big" + "math/rand/v2" "net" "net/netip" "time" @@ -23,7 +24,11 @@ import ( "github.com/alexeykiselev/waves-fork-detector/peers" ) -const pingInterval = 1 * time.Minute +const ( + pingInterval = 1 * time.Minute + // maxPeersInMessage is the limit of addresses in Peers message, exceeding it gets the sender blacklisted. + maxPeersInMessage = 1000 +) type Distributor struct { ctx context.Context @@ -241,19 +246,24 @@ func (d *Distributor) handlePeersMessage(pm *proto.PeersMessage) { func (d *Distributor) handleGetPeersMessage(peer peer.Peer) { d.logger.Debug("Get peers request received", slog.String("peer", peer.RemoteAddr().String())) - friendlyPeers, err := d.registry.FriendlyPeers() + activePeers, err := d.registry.ActivePeers() if err != nil { d.logger.Warn("Failed to get peers", logging.Error(err)) return } - infos := make([]proto.PeerInfo, 0, len(friendlyPeers)) - for _, p := range friendlyPeers { + infos := make([]proto.PeerInfo, 0, len(activePeers)) + for _, p := range activePeers { pi := proto.PeerInfo{ Addr: p.TCPAddr().IP, Port: p.AddressPort.Port(), } infos = append(infos, pi) } + if len(infos) > maxPeersInMessage { + swap := func(i, j int) { infos[i], infos[j] = infos[j], infos[i] } + rand.Shuffle(len(infos), swap) //nolint:gosec // No need for crypto random here. + infos = infos[:maxPeersInMessage] + } peersMessage := &proto.PeersMessage{ Peers: infos, } diff --git a/forkdetector.go b/forkdetector.go index a7366f1..3ddeebc 100644 --- a/forkdetector.go +++ b/forkdetector.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "log/slog" + "net" "os" "os/signal" "syscall" @@ -69,10 +70,7 @@ func run() error { } }(reg) - n := reg.AppendAddresses(p.seedPeers) - if n > 0 { - slog.Info("Seed peers added to storage", slog.Int("count", n)) - } + initializePeers(reg, p.seedPeers, newLogger(h, registryNamespace)) linkage, err := chains.NewLinkage(p.dbPath, p.scheme, p.genesis, newLogger(h, linkageNamespace)) if err != nil { @@ -121,6 +119,19 @@ func run() error { return nil } +func initializePeers(reg *peers.Registry, seeds []*net.TCPAddr, logger *slog.Logger) { + // Protect configured seeds before pruning existing records. + if added := reg.AppendSeedAddresses(seeds); added > 0 { + logger.Info("Seed peers added to storage", slog.Int("count", added)) + } + n, err := reg.PruneStalePeers() + if err != nil { + logger.Warn("Failed to prune stale peers", logging.Error(err)) + } else if n > 0 { + logger.Info("Stale peers removed", slog.Int("count", n)) + } +} + func buildLogger(h slog.Handler, namespace string, enabled bool) *slog.Logger { if !enabled { return slog.New(slog.DiscardHandler) diff --git a/forkdetector_internal_test.go b/forkdetector_internal_test.go new file mode 100644 index 0000000..14123cd --- /dev/null +++ b/forkdetector_internal_test.go @@ -0,0 +1,64 @@ +package main + +import ( + "log/slog" + "net" + "net/netip" + "path/filepath" + "testing" + "time" + + "github.com/fxamacker/cbor/v2" + "github.com/stretchr/testify/require" + "github.com/syndtr/goleveldb/leveldb" + "github.com/wavesplatform/gowaves/pkg/proto" + + "github.com/alexeykiselev/waves-fork-detector/peers" +) + +func TestInitializePeersPreservesExpiredSeeds(t *testing.T) { + path := t.TempDir() + seed := netip.MustParseAddrPort("1.1.1.1:6868") + stale := netip.MustParseAddrPort("2.2.2.2:6868") + fresh := netip.MustParseAddrPort("3.3.3.3:6868") + + // Persist records from a previous run using the peers storage CBOR schema. + db, err := leveldb.OpenFile(filepath.Join(path, "peers"), nil) + require.NoError(t, err) + for _, ap := range []netip.AddrPort{seed, stale, fresh} { + lastSeen := time.Now().Add(-31 * 24 * time.Hour) + if ap == fresh { + lastSeen = time.Now() + } + value, marshalErr := cbor.Marshal(map[uint64]any{ + 0: ap.Port(), + 3: "1.5.0", + 4: peers.PeerConnected, + 7: lastSeen, + }) + require.NoError(t, marshalErr) + key := ap.Addr().As4() + require.NoError(t, db.Put(key[:], value, nil)) + } + require.NoError(t, db.Close()) + + logger := slog.New(slog.DiscardHandler) + reg, err := peers.NewRegistry(proto.TestNetScheme, proto.TCPAddr{}, + []proto.Version{proto.NewVersion(1, 5, 0)}, path, logger) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, reg.Close()) }) + + initializePeers(reg, []*net.TCPAddr{net.TCPAddrFromAddrPort(seed)}, logger) + // A subsequent timer-driven prune must also retain the disconnected seed. + removed, err := reg.PruneStalePeers() + require.NoError(t, err) + require.Zero(t, removed) + + available, err := reg.TakeAvailableAddresses() + require.NoError(t, err) + require.ElementsMatch(t, []netip.AddrPort{seed, fresh}, available) + restored, err := reg.Peer(seed.Addr()) + require.NoError(t, err) + require.Equal(t, peers.PeerConnected, restored.State) + require.True(t, restored.LastSeen.Before(time.Now().Add(-30*24*time.Hour))) +} diff --git a/peers/peer.go b/peers/peer.go index f628969..07a10ba 100644 --- a/peers/peer.go +++ b/peers/peer.go @@ -28,6 +28,7 @@ type Peer struct { State State `json:"state"` NextAttempt time.Time `json:"next_attempt"` Score *big.Int `json:"score"` + LastSeen time.Time `json:"last_seen"` p peer.Peer logger *slog.Logger } diff --git a/peers/registry.go b/peers/registry.go index 503b301..0bf79b4 100644 --- a/peers/registry.go +++ b/peers/registry.go @@ -20,6 +20,10 @@ import ( const ( delay = 10 * time.Minute + // activeWindow is the period during which a peer is considered active after it was last seen. + activeWindow = 96 * time.Hour + // staleAge is the period after which a peer that was not seen is removed from the storage. + staleAge = 30 * 24 * time.Hour ) type Registry struct { @@ -31,6 +35,7 @@ type Registry struct { mu sync.Mutex connections map[netip.Addr]peer.Peer pending map[netip.Addr]struct{} + seeds map[netip.Addr]struct{} logger *slog.Logger } @@ -56,6 +61,7 @@ func NewRegistry( storage: s, connections: make(map[netip.Addr]peer.Peer), pending: make(map[netip.Addr]struct{}), + seeds: make(map[netip.Addr]struct{}), logger: logger, }, nil } @@ -86,15 +92,17 @@ func (r *Registry) RegisterPeer(addr netip.Addr, np peer.Peer, handshake proto.H _ = np.Close() return fmt.Errorf("failed to register peer: %w", err) } - if p.State == PeerHostile { - _ = np.Close() - return fmt.Errorf("peer '%s' already registered as hostile", addr.String()) - } - p = Peer{} + p = Peer{AddressPort: netip.AddrPortFrom(addr, 0)} + } + if p.State == PeerHostile { + _ = np.Close() + return fmt.Errorf("peer '%s' already registered as hostile", addr.String()) } + now := time.Now().Round(time.Second) if np.Handshake().Version.CmpMinor(p.Version) >= 2 { p.State = PeerHostile + p.LastSeen = now p.Version = np.Handshake().Version p.Name = fmt.Sprintf("%s(%s)", np.Handshake().NodeName, np.Handshake().AppName) _ = np.Close() @@ -102,6 +110,7 @@ func (r *Registry) RegisterPeer(addr netip.Addr, np peer.Peer, handshake proto.H } if np.Handshake().AppName[len(np.Handshake().AppName)-1] != r.scheme { p.State = PeerHostile + p.LastSeen = now p.Version = np.Handshake().Version p.Name = fmt.Sprintf("%s(%s)", np.Handshake().NodeName, np.Handshake().AppName) _ = np.Close() @@ -117,7 +126,8 @@ func (r *Registry) RegisterPeer(addr netip.Addr, np peer.Peer, handshake proto.H p.Name = handshake.NodeName p.Version = handshake.Version p.State = PeerConnected - p.NextAttempt = time.Now().Round(time.Second) + p.NextAttempt = now + p.LastSeen = now p.p = np r.connections[addr] = np @@ -134,6 +144,7 @@ func (r *Registry) UpdatePeerScore(addr netip.Addr, score *big.Int) error { return fmt.Errorf("failed to update peer score: %w", err) } p.Score = score + p.LastSeen = time.Now().Round(time.Second) return r.storage.putPeer(p) } @@ -175,7 +186,11 @@ func (r *Registry) UnregisterPeer(addr netip.Addr) error { } delete(r.pending, addr) - delete(r.connections, addr) + if _, ok := r.connections[addr]; ok { + // The peer was alive until this moment. + p.LastSeen = time.Now().Round(time.Second) + delete(r.connections, addr) + } p.NextAttempt = time.Now().Add(delay).Round(time.Second) p.p = nil @@ -203,6 +218,7 @@ func (r *Registry) MarkAsHostile(addr net.Addr) error { p.AddressPort = netip.AddrPortFrom(a, 0) p.State = PeerHostile + p.LastSeen = time.Now().Round(time.Second) return r.storage.putPeer(p) } @@ -230,6 +246,16 @@ func (r *Registry) Connections() ([]Peer, error) { // AppendAddresses adds new addresses to the storage filtering out already known addresses. // Function returns the number of newly added addresses. func (r *Registry) AppendAddresses(addresses []*net.TCPAddr) int { + return r.appendAddresses(addresses, false) +} + +// AppendSeedAddresses adds configured seeds and exempts them from stale-peer pruning for this run. +// Existing peer state and retry times are preserved. It returns the number of newly added addresses. +func (r *Registry) AppendSeedAddresses(addresses []*net.TCPAddr) int { + return r.appendAddresses(addresses, true) +} + +func (r *Registry) appendAddresses(addresses []*net.TCPAddr, seeds bool) int { r.mu.Lock() defer r.mu.Unlock() @@ -252,6 +278,9 @@ func (r *Registry) AppendAddresses(addresses []*net.TCPAddr) int { r.logger.Debug("Skipping self address", slog.String("address", ap.String())) continue } + if seeds { + r.seeds[ap.Addr()] = struct{}{} + } yes, err := r.storage.hasPeer(ap.Addr()) if err != nil { r.logger.Debug("Failed to append addresses", logging.Error(err)) @@ -261,6 +290,7 @@ func (r *Registry) AppendAddresses(addresses []*net.TCPAddr) int { p := Peer{ AddressPort: ap, State: PeerUnknown, + LastSeen: time.Now().Round(time.Second), } if putErr := r.storage.putPeer(p); putErr != nil { r.logger.Warn("Failed to append addresses", logging.Error(putErr)) @@ -309,6 +339,75 @@ func (r *Registry) FriendlyPeers() ([]Peer, error) { return friends, nil } +// ActivePeers returns the peers suitable for advertising to other nodes: successfully connected at least once, +// with known listening port, and either connected right now or seen during the active window. +func (r *Registry) ActivePeers() ([]Peer, error) { + r.mu.Lock() + defer r.mu.Unlock() + + peers, err := r.storage.peers() + if err != nil { + return nil, fmt.Errorf("failed to get active peers: %w", err) + } + threshold := time.Now().Add(-activeWindow) + active := make([]Peer, 0) + for _, p := range peers { + if p.State != PeerConnected || p.AddressPort.Port() == 0 { + continue + } + if _, ok := r.connections[p.AddressPort.Addr()]; ok || p.LastSeen.After(threshold) { + active = append(active, p) + } + } + return active, nil +} + +// PruneStalePeers removes the peers that were not seen longer than the stale age. +// Configured seeds are retained regardless of age. +// Peers without the last seen time (stored by previous versions) get the current time to start counting from. +// Function returns the number of removed peers. +func (r *Registry) PruneStalePeers() (int, error) { + r.mu.Lock() + defer r.mu.Unlock() + + peers, err := r.storage.peers() + if err != nil { + return 0, fmt.Errorf("failed to prune stale peers: %w", err) + } + now := time.Now().Round(time.Second) + threshold := now.Add(-staleAge) + stale := make([]netip.Addr, 0) + for _, p := range peers { + addr := p.AddressPort.Addr() + if _, ok := r.seeds[addr]; ok { + continue + } + if _, ok := r.connections[addr]; ok { + continue + } + if _, ok := r.pending[addr]; ok { + continue + } + if p.LastSeen.IsZero() { + p.LastSeen = now + if putErr := r.storage.putPeer(p); putErr != nil { + return 0, fmt.Errorf("failed to prune stale peers: %w", putErr) + } + continue + } + if p.LastSeen.Before(threshold) { + stale = append(stale, addr) + } + } + if len(stale) == 0 { + return 0, nil + } + if dErr := r.storage.deletePeers(stale); dErr != nil { + return 0, fmt.Errorf("failed to prune stale peers: %w", dErr) + } + return len(stale), nil +} + func (r *Registry) Addresses() ([]net.Addr, error) { addresses := make([]net.Addr, 0) peers, err := r.FriendlyPeers() diff --git a/peers/registry_internal_test.go b/peers/registry_internal_test.go index d0230dd..2e63b89 100644 --- a/peers/registry_internal_test.go +++ b/peers/registry_internal_test.go @@ -1,9 +1,14 @@ package peers import ( + "log/slog" + "net" + "net/netip" "testing" + "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/wavesplatform/gowaves/pkg/proto" ) @@ -54,3 +59,115 @@ func TestNextVersionZero(t *testing.T) { v := vs.nextVersion(proto.Version{}) assert.Equal(t, proto.NewVersion(1, 5, 0), v) } + +func newTestRegistry(t *testing.T) *Registry { + r, err := NewRegistry(proto.TestNetScheme, proto.TCPAddr{}, []proto.Version{proto.NewVersion(1, 5, 0)}, + t.TempDir(), slog.New(slog.DiscardHandler)) + require.NoError(t, err) + t.Cleanup(func() { _ = r.Close() }) + return r +} + +func putTestPeer(t *testing.T, r *Registry, ap string, state State, lastSeen time.Time) { + p := Peer{ + AddressPort: netip.MustParseAddrPort(ap), + Version: proto.NewVersion(1, 5, 0), + State: state, + LastSeen: lastSeen, + } + require.NoError(t, r.storage.putPeer(p)) +} + +func addresses(peers []Peer) []string { + r := make([]string, len(peers)) + for i, p := range peers { + r[i] = p.AddressPort.String() + } + return r +} + +func TestActivePeers(t *testing.T) { + r := newTestRegistry(t) + now := time.Now().Round(time.Second) + putTestPeer(t, r, "1.1.1.1:6868", PeerConnected, now.Add(-time.Hour)) // Recently seen. + putTestPeer(t, r, "2.2.2.2:6868", PeerConnected, now.Add(-100*time.Hour)) // Seen too long ago. + putTestPeer(t, r, "3.3.3.3:0", PeerConnected, now.Add(-time.Hour)) // Unknown port. + putTestPeer(t, r, "4.4.4.4:6868", PeerUnknown, now.Add(-time.Hour)) // Never connected. + putTestPeer(t, r, "5.5.5.5:6868", PeerHostile, now.Add(-time.Hour)) // Hostile. + putTestPeer(t, r, "6.6.6.6:6868", PeerConnected, time.Time{}) // Legacy record. + putTestPeer(t, r, "7.7.7.7:6868", PeerConnected, now.Add(-1000*time.Hour)) // Connected right now. + r.connections[netip.MustParseAddr("7.7.7.7")] = nil + + active, err := r.ActivePeers() + require.NoError(t, err) + assert.ElementsMatch(t, []string{"1.1.1.1:6868", "7.7.7.7:6868"}, addresses(active)) +} + +func TestPruneStalePeers(t *testing.T) { + r := newTestRegistry(t) + now := time.Now().Round(time.Second) + old := now.Add(-31 * 24 * time.Hour) + putTestPeer(t, r, "1.1.1.1:6868", PeerConnected, now.Add(-time.Hour)) // Fresh. + putTestPeer(t, r, "2.2.2.2:6868", PeerConnected, old) // Stale. + putTestPeer(t, r, "3.3.3.3:6868", PeerUnknown, old) // Stale, never connected. + putTestPeer(t, r, "4.4.4.4:6868", PeerHostile, old) // Stale hostile. + putTestPeer(t, r, "5.5.5.5:6868", PeerConnected, time.Time{}) // Legacy record, gets backfilled. + putTestPeer(t, r, "6.6.6.6:6868", PeerConnected, old) // Connected right now. + putTestPeer(t, r, "7.7.7.7:6868", PeerUnknown, old) // Pending connection. + r.connections[netip.MustParseAddr("6.6.6.6")] = nil + r.pending[netip.MustParseAddr("7.7.7.7")] = struct{}{} + + n, err := r.PruneStalePeers() + require.NoError(t, err) + assert.Equal(t, 3, n) + + all, err := r.storage.peers() + require.NoError(t, err) + assert.ElementsMatch(t, []string{"1.1.1.1:6868", "5.5.5.5:6868", "6.6.6.6:6868", "7.7.7.7:6868"}, addresses(all)) + + legacy, err := r.storage.peer(netip.MustParseAddr("5.5.5.5")) + require.NoError(t, err) + assert.False(t, legacy.LastSeen.IsZero()) +} + +func TestPruneStalePeersRetainsSeeds(t *testing.T) { + r := newTestRegistry(t) + old := time.Now().Add(-31 * 24 * time.Hour).Round(time.Second) + seed := netip.MustParseAddrPort("1.1.1.1:6868") + backoff := netip.MustParseAddrPort("2.2.2.2:6868") + hostile := netip.MustParseAddrPort("3.3.3.3:6868") + putTestPeer(t, r, seed.String(), PeerConnected, old) + putTestPeer(t, r, backoff.String(), PeerUnknown, old) + putTestPeer(t, r, hostile.String(), PeerHostile, old) + putTestPeer(t, r, "4.4.4.4:6868", PeerConnected, old) + p, err := r.Peer(backoff.Addr()) + require.NoError(t, err) + p.NextAttempt = time.Now().Add(time.Hour).Round(time.Second) + require.NoError(t, r.storage.putPeer(p)) + + require.Zero(t, r.AppendSeedAddresses([]*net.TCPAddr{ + net.TCPAddrFromAddrPort(seed), net.TCPAddrFromAddrPort(backoff), net.TCPAddrFromAddrPort(hostile), + })) + for _, wantRemoved := range []int{1, 0} { + removed, pruneErr := r.PruneStalePeers() + require.NoError(t, pruneErr) + require.Equal(t, wantRemoved, removed) + } + all, err := r.Peers() + require.NoError(t, err) + require.ElementsMatch(t, []string{seed.String(), backoff.String(), hostile.String()}, addresses(all)) + for _, retained := range all { + require.Equal(t, old, retained.LastSeen) + } + retained, err := r.Peer(backoff.Addr()) + require.NoError(t, err) + require.Equal(t, p.NextAttempt, retained.NextAttempt) + _, err = r.SuggestVersion(hostile.Addr()) + require.Error(t, err) + active, err := r.ActivePeers() + require.NoError(t, err) + require.Empty(t, active) + available, err := r.TakeAvailableAddresses() + require.NoError(t, err) + require.Equal(t, []netip.AddrPort{seed}, available) +} diff --git a/peers/storage.go b/peers/storage.go index b653beb..e1a164c 100644 --- a/peers/storage.go +++ b/peers/storage.go @@ -47,6 +47,7 @@ type value struct { State State `cbor:"4,keyasint"` NextAttempt time.Time `cbor:"5,keyasint"` Score *big.Int `cbor:"6,keyasint"` + LastSeen time.Time `cbor:"7,keyasint"` } type storage struct { @@ -78,7 +79,7 @@ func (s *storage) peer(addr netip.Addr) (Peer, error) { } val := new(value) if umErr := cbor.Unmarshal(v, val); umErr != nil { - return Peer{}, err + return Peer{}, umErr } ver, err := proto.NewVersionFromString(val.Version) if err != nil { @@ -92,6 +93,7 @@ func (s *storage) peer(addr netip.Addr) (Peer, error) { State: val.State, NextAttempt: val.NextAttempt, Score: val.Score, + LastSeen: val.LastSeen, }, nil } @@ -112,6 +114,7 @@ func (s *storage) putPeer(peer Peer) error { State: peer.State, NextAttempt: peer.NextAttempt, Score: peer.Score, + LastSeen: peer.LastSeen, } b, err := cbor.Marshal(v) if err != nil { @@ -149,12 +152,22 @@ func (s *storage) peers() ([]Peer, error) { NextAttempt: v.NextAttempt, State: v.State, Score: v.Score, + LastSeen: v.LastSeen, } r = append(r, p) } return r, nil } +func (s *storage) deletePeers(addrs []netip.Addr) error { + batch := new(leveldb.Batch) + for _, a := range addrs { + k := key{addr: a} + batch.Delete(k.bytes()) + } + return s.db.Write(batch, nil) +} + func (s *storage) hasPeer(addr netip.Addr) (bool, error) { sn, err := s.db.GetSnapshot() if err != nil { diff --git a/peers/storage_internal_test.go b/peers/storage_internal_test.go index d6515de..8456c3a 100644 --- a/peers/storage_internal_test.go +++ b/peers/storage_internal_test.go @@ -23,6 +23,7 @@ func TestPutGetPeer(t *testing.T) { State: 1, NextAttempt: time.Now().Add(time.Hour).Round(time.Second), Score: big.NewInt(123), + LastSeen: time.Now().Add(-time.Hour).Round(time.Second), p: nil, } err = st.putPeer(p) diff --git a/peers/version_provider.go b/peers/version_provider.go index 2048416..bdfe64e 100644 --- a/peers/version_provider.go +++ b/peers/version_provider.go @@ -31,6 +31,7 @@ func (r *Registry) SuggestVersion(addr netip.Addr) (proto.Version, error) { Version: ver, NextAttempt: time.Now().Add(delay).Round(time.Second), State: PeerUnknown, + LastSeen: time.Now().Round(time.Second), } if putErr := r.storage.putPeer(np); putErr != nil { return proto.Version{}, fmt.Errorf("failed to save peer: %w", putErr) diff --git a/respawn.go b/respawn.go index 30ac8b5..94e30da 100644 --- a/respawn.go +++ b/respawn.go @@ -16,7 +16,10 @@ import ( "github.com/alexeykiselev/waves-fork-detector/peers" ) -const respawnInterval = 10 * time.Second +const ( + respawnInterval = 10 * time.Second + pruneInterval = 1 * time.Hour +) type Respawn struct { ctx context.Context @@ -54,10 +57,14 @@ func (r *Respawn) Shutdown() { } func (r *Respawn) handleEvents() error { + pruneTicker := time.NewTicker(pruneInterval) + defer pruneTicker.Stop() for { select { case <-r.ctx.Done(): return nil + case <-pruneTicker.C: + r.pruneStalePeers() case <-r.timer.C: addresses, err := r.reg.TakeAvailableAddresses() if len(addresses) > 0 { @@ -76,6 +83,17 @@ func (r *Respawn) handleEvents() error { } } +func (r *Respawn) pruneStalePeers() { + n, err := r.reg.PruneStalePeers() + if err != nil { + r.logger.Warn("Failed to prune stale peers", logging.Error(err)) + return + } + if n > 0 { + r.logger.Info("Stale peers removed", slog.Int("count", n)) + } +} + func (r *Respawn) establishConnections(addresses []netip.AddrPort) { for _, a := range addresses { go func(ap netip.AddrPort) { From 0142aa5cd4444919bb66c2b1d56884b3bdd95fc3 Mon Sep 17 00:00:00 2001 From: Alexey Kiselev Date: Thu, 1 Oct 2026 20:26:03 +0400 Subject: [PATCH 7/9] Proper API startup error handling. --- api/api.go | 5 ++++ api/api_internal_test.go | 25 ++++++++++++++++++ forkdetector.go | 12 +++++++-- forkdetector_internal_test.go | 48 +++++++++++++++++++++++++++++++++++ listener.go | 15 ++++++----- 5 files changed, 96 insertions(+), 9 deletions(-) create mode 100644 api/api_internal_test.go diff --git a/api/api.go b/api/api.go index 893d2c5..7afcc9c 100644 --- a/api/api.go +++ b/api/api.go @@ -101,6 +101,11 @@ func (a *API) Run(ctx context.Context) { g.Go(a.runServer) } +// Wait waits for the API server started by Run to stop and returns its error. +func (a *API) Wait() error { + return a.wait() +} + func (a *API) Shutdown() { if err := a.srv.Shutdown(a.ctx); err != nil && !errors.Is(err, context.Canceled) { a.logger.Error("Failed to shutdown API", logging.Error(err)) diff --git a/api/api_internal_test.go b/api/api_internal_test.go new file mode 100644 index 0000000..d987cbc --- /dev/null +++ b/api/api_internal_test.go @@ -0,0 +1,25 @@ +package api + +import ( + "log/slog" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestWaitAfterServerClose(t *testing.T) { + a, err := NewAPI(nil, nil, "127.0.0.1:0", slog.New(slog.DiscardHandler)) + require.NoError(t, err) + a.Run(t.Context()) + require.NoError(t, a.srv.Close()) + done := make(chan error, 1) + go func() { done <- a.Wait() }() + select { + case waitErr := <-done: + require.NoError(t, waitErr) + case <-time.After(5 * time.Second): + t.Fatal("API did not stop after closing the server") + } + a.Shutdown() +} diff --git a/forkdetector.go b/forkdetector.go index 3ddeebc..ba6e85f 100644 --- a/forkdetector.go +++ b/forkdetector.go @@ -11,6 +11,7 @@ import ( "github.com/wavesplatform/gowaves/pkg/logging" "github.com/wavesplatform/gowaves/pkg/p2p/peer" + "golang.org/x/sync/errgroup" "github.com/alexeykiselev/waves-fork-detector/api" "github.com/alexeykiselev/waves-fork-detector/chains" @@ -55,6 +56,7 @@ func run() error { ctx, done := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer done() + g, ctx := errgroup.WithContext(ctx) slog.Info("Waves Fork Detector", slog.String("version", version.ForkDetectorVersion())) p.log() @@ -85,6 +87,12 @@ func run() error { return fmt.Errorf("failed to create API server: %w", err) } a.Run(ctx) + g.Go(func() error { + if apiErr := a.Wait(); apiErr != nil { + return fmt.Errorf("API server failed: %w", apiErr) + } + return nil + }) parent := peer.NewParent(true) nl := buildLogger(h, netNamespace, p.logNetwork) @@ -106,7 +114,7 @@ func run() error { loader.Run(ctx) <-ctx.Done() - slog.Info("User termination in progress...") + slog.Info("Termination in progress...") a.Shutdown() listener.Shutdown() @@ -116,7 +124,7 @@ func run() error { slog.Info("Terminated") - return nil + return g.Wait() } func initializePeers(reg *peers.Registry, seeds []*net.TCPAddr, logger *slog.Logger) { diff --git a/forkdetector_internal_test.go b/forkdetector_internal_test.go index 14123cd..e8e6e60 100644 --- a/forkdetector_internal_test.go +++ b/forkdetector_internal_test.go @@ -1,9 +1,13 @@ package main import ( + "context" + "flag" "log/slog" "net" "net/netip" + "os" + "os/exec" "path/filepath" "testing" "time" @@ -16,6 +20,50 @@ import ( "github.com/alexeykiselev/waves-fork-detector/peers" ) +func TestAPIBindFailureTerminatesProcess(t *testing.T) { + var cfg net.ListenConfig + occupied, err := cfg.Listen(t.Context(), "tcp", "127.0.0.1:0") + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, occupied.Close()) }) + executable, err := os.Executable() + require.NoError(t, err) + for _, tc := range []struct { + name string + bind string + declared string + }{ + {name: "no peer listener", bind: "127.0.0.1:0"}, + {name: "starting peer listener", bind: "127.0.0.1:0", declared: "127.0.0.1:6868"}, + {name: "failed peer listener", bind: occupied.Addr().String(), declared: "127.0.0.1:6868"}, + } { + t.Run(tc.name, func(t *testing.T) { + ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second) + defer cancel() + cmd := exec.CommandContext(ctx, executable, "-test.run=^TestAPIProcessHelper$", "--", + "-db", t.TempDir(), "-api", occupied.Addr().String(), + "-net", tc.bind, "-declared-address", tc.declared) + cmd.Env = append(os.Environ(), "FORK_DETECTOR_API_PROCESS_TEST=1") + output, runErr := cmd.CombinedOutput() + require.NoError(t, ctx.Err(), "process did not terminate: %s", output) + var exitErr *exec.ExitError + require.ErrorAs(t, runErr, &exitErr, "%s", output) + require.Equal(t, 1, exitErr.ExitCode(), "%s", output) + require.Contains(t, string(output), "API server failed") + require.Contains(t, string(output), "Terminated") + }) + } +} + +func TestAPIProcessHelper(_ *testing.T) { + if os.Getenv("FORK_DETECTOR_API_PROCESS_TEST") != "1" { + return + } + // Isolate application flags and os.Exit in the child process. + flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError) + os.Args = append(os.Args[:1], os.Args[3:]...) + os.Exit(realMain()) +} + func TestInitializePeersPreservesExpiredSeeds(t *testing.T) { path := t.TempDir() seed := netip.MustParseAddrPort("1.1.1.1:6868") diff --git a/listener.go b/listener.go index 3b7a31b..c3dcf0b 100644 --- a/listener.go +++ b/listener.go @@ -19,7 +19,6 @@ type Listener struct { declared proto.TCPAddr cm *ConnectionManager - nl net.Listener logger *slog.Logger } @@ -53,10 +52,6 @@ func (l *Listener) Run(ctx context.Context) { } func (l *Listener) Shutdown() { - if err := l.nl.Close(); err != nil { - l.logger.Error("Failed to close listener", slog.String("bind", l.bind.String()), logging.Error(err)) - return - } if err := l.wait(); err != nil { l.logger.Warn("Failed to shutdown Listener", logging.Error(err)) } @@ -70,15 +65,21 @@ func (l *Listener) run() error { if err != nil { return err } - l.nl = nl + defer func() { _ = nl.Close() }() + // Unblock Accept when the shared context is canceled. + stop := context.AfterFunc(l.ctx, func() { _ = nl.Close() }) + defer stop() for { select { case <-l.ctx.Done(): return nil default: - conn, acErr := l.nl.Accept() + conn, acErr := nl.Accept() if acErr != nil { + if l.ctx.Err() != nil { + return nil + } l.logger.Error("Failed to accept connection", logging.Error(acErr)) continue } From 0ecd90ca8122024d943d169ad34e7552b17ffbc1 Mon Sep 17 00:00:00 2001 From: Alexey Kiselev Date: Fri, 2 Oct 2026 11:18:02 +0400 Subject: [PATCH 8/9] Review issues fixed. --- README.md | 12 +++++++++--- api/swagger/openapi.yaml | 9 +++++++++ forkdetector.go | 6 ++++++ forkdetector_internal_test.go | 17 +++++++++++------ listener.go | 6 ++++++ service.go | 3 +++ 6 files changed, 44 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 73e5a74..87a3a6a 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,8 @@ Reply example: "version": "0.0.0", "state": 0, "next_attempt": "0001-01-01T00:00:00Z", - "score": null + "score": null, + "last_seen": "2024-11-06T12:26:11Z" } ] ``` @@ -72,6 +73,9 @@ Reply fields: * `2` `Hostile` - successful connection was established, but the network byte or version is not acceptable. * `next_attempt` - time when the next attempt to connect to the node is planned. This field is used during version selection (see the section [Version Selection](#version-selection)). * `score` - last known value of the Score; `null` value if there was no connection. +* `last_seen` - time of the last activity of the node: the last successful handshake, Score update or disconnection. For a node that was never connected, it is the time when its address was discovered. + +Nodes that were not seen for 30 days are removed from storage, except the configured seed peers. When other nodes request known peers, Fork Detector replies only with nodes connected at the moment or seen within the last 96 hours (at most 1000 addresses). ### `GET` `/api/peers/friendly` @@ -87,7 +91,8 @@ Reply example: "version": "1.5.8", "state": 1, "next_attempt": "2024-11-06T12:36:11Z", - "score": 967128354984173501468049 + "score": 967128354984173501468049, + "last_seen": "2024-11-06T12:26:11Z" } ] ``` @@ -108,7 +113,8 @@ Reply example: "version": "1.5.8", "state": 1, "next_attempt": "2024-11-06T12:36:11Z", - "score": 967129143986158086525130 + "score": 967129143986158086525130, + "last_seen": "2024-11-06T12:26:11Z" } ] ``` diff --git a/api/swagger/openapi.yaml b/api/swagger/openapi.yaml index 0b2406e..3ca60fd 100644 --- a/api/swagger/openapi.yaml +++ b/api/swagger/openapi.yaml @@ -252,6 +252,15 @@ components: nullable: true example: null description: Last known Score value; `null` if there was no connection. + last_seen: + type: string + format: date-time + example: "2024-11-06T12:26:11Z" + description: |- + Time of the last activity of the node: the last successful handshake, Score update or disconnection. + For a node that was never connected, it is the time when its address was discovered. + Only nodes connected at the moment or seen within the last 96 hours are advertised to other nodes. + Nodes not seen for 30 days are removed from storage, except the configured seed peers. Head: type: object diff --git a/forkdetector.go b/forkdetector.go index ba6e85f..e0f6177 100644 --- a/forkdetector.go +++ b/forkdetector.go @@ -102,6 +102,12 @@ func run() error { listener := NewListener(p.netBind, p.declaredAddress, connManger, newLogger(h, listenerNamespace)) listener.Run(ctx) + g.Go(func() error { + if lErr := listener.Wait(); lErr != nil { + return fmt.Errorf("network server failed: %w", lErr) + } + return nil + }) respawn := NewRespawn(reg, connManger, newLogger(h, respawnNamespace)) respawn.Run(ctx) diff --git a/forkdetector_internal_test.go b/forkdetector_internal_test.go index e8e6e60..65df2ad 100644 --- a/forkdetector_internal_test.go +++ b/forkdetector_internal_test.go @@ -20,7 +20,7 @@ import ( "github.com/alexeykiselev/waves-fork-detector/peers" ) -func TestAPIBindFailureTerminatesProcess(t *testing.T) { +func TestBindFailureTerminatesProcess(t *testing.T) { var cfg net.ListenConfig occupied, err := cfg.Listen(t.Context(), "tcp", "127.0.0.1:0") require.NoError(t, err) @@ -29,18 +29,23 @@ func TestAPIBindFailureTerminatesProcess(t *testing.T) { require.NoError(t, err) for _, tc := range []struct { name string + api string bind string declared string + expected string }{ - {name: "no peer listener", bind: "127.0.0.1:0"}, - {name: "starting peer listener", bind: "127.0.0.1:0", declared: "127.0.0.1:6868"}, - {name: "failed peer listener", bind: occupied.Addr().String(), declared: "127.0.0.1:6868"}, + {name: "failed API without peer listener", api: occupied.Addr().String(), bind: "127.0.0.1:0", + expected: "API server failed"}, + {name: "failed API with starting peer listener", api: occupied.Addr().String(), bind: "127.0.0.1:0", + declared: "127.0.0.1:6868", expected: "API server failed"}, + {name: "failed peer listener", api: "127.0.0.1:0", bind: occupied.Addr().String(), + declared: "127.0.0.1:6868", expected: "network server failed"}, } { t.Run(tc.name, func(t *testing.T) { ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second) defer cancel() cmd := exec.CommandContext(ctx, executable, "-test.run=^TestAPIProcessHelper$", "--", - "-db", t.TempDir(), "-api", occupied.Addr().String(), + "-db", t.TempDir(), "-api", tc.api, "-net", tc.bind, "-declared-address", tc.declared) cmd.Env = append(os.Environ(), "FORK_DETECTOR_API_PROCESS_TEST=1") output, runErr := cmd.CombinedOutput() @@ -48,7 +53,7 @@ func TestAPIBindFailureTerminatesProcess(t *testing.T) { var exitErr *exec.ExitError require.ErrorAs(t, runErr, &exitErr, "%s", output) require.Equal(t, 1, exitErr.ExitCode(), "%s", output) - require.Contains(t, string(output), "API server failed") + require.Contains(t, string(output), tc.expected) require.Contains(t, string(output), "Terminated") }) } diff --git a/listener.go b/listener.go index c3dcf0b..d4f7632 100644 --- a/listener.go +++ b/listener.go @@ -51,6 +51,11 @@ func (l *Listener) Run(ctx context.Context) { g.Go(l.run) } +// Wait waits for the listener started by Run to stop and returns its error. +func (l *Listener) Wait() error { + return l.wait() +} + func (l *Listener) Shutdown() { if err := l.wait(); err != nil { l.logger.Warn("Failed to shutdown Listener", logging.Error(err)) @@ -63,6 +68,7 @@ func (l *Listener) run() error { var cfg net.ListenConfig nl, err := cfg.Listen(l.ctx, "tcp", l.bind.String()) if err != nil { + l.logger.Error("Failed to start listening", logging.Error(err)) return err } defer func() { _ = nl.Close() }() diff --git a/service.go b/service.go index fad54ff..7dc2342 100644 --- a/service.go +++ b/service.go @@ -4,6 +4,7 @@ import "context" type Service interface { Run(ctx context.Context) + Wait() error Shutdown() } @@ -11,4 +12,6 @@ type EmptyService struct{} func (l *EmptyService) Run(_ context.Context) {} +func (l *EmptyService) Wait() error { return nil } + func (l *EmptyService) Shutdown() {} From 0181702e1a004537a06b0ee4fcb70903abca52d7 Mon Sep 17 00:00:00 2001 From: Alexey Kiselev Date: Fri, 2 Oct 2026 11:37:00 +0400 Subject: [PATCH 9/9] More review fixes. --- README.md | 4 ++- api/swagger/openapi.yaml | 1 + peers/registry.go | 15 +++++----- peers/registry_internal_test.go | 51 +++++++++++++++++++++++++++++++++ 4 files changed, 63 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 87a3a6a..eb8b590 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,9 @@ Reply fields: * `score` - last known value of the Score; `null` value if there was no connection. * `last_seen` - time of the last activity of the node: the last successful handshake, Score update or disconnection. For a node that was never connected, it is the time when its address was discovered. -Nodes that were not seen for 30 days are removed from storage, except the configured seed peers. When other nodes request known peers, Fork Detector replies only with nodes connected at the moment or seen within the last 96 hours (at most 1000 addresses). +Legacy records without `last_seen`, including configured seeds, receive the current time during pruning after an upgrade. + +Pruning runs at startup and every hour. Nodes that were not seen for 30 days are removed from storage, except configured seed peers and peers with active or pending connections. When other nodes request known peers, Fork Detector replies only with nodes that have connected successfully at least once, have a known listening port, and are connected at the moment or were seen within the last 96 hours (at most 1000 addresses). ### `GET` `/api/peers/friendly` diff --git a/api/swagger/openapi.yaml b/api/swagger/openapi.yaml index 3ca60fd..c75f591 100644 --- a/api/swagger/openapi.yaml +++ b/api/swagger/openapi.yaml @@ -259,6 +259,7 @@ components: description: |- Time of the last activity of the node: the last successful handshake, Score update or disconnection. For a node that was never connected, it is the time when its address was discovered. + Legacy records without this field, including configured seeds, receive the current time during pruning after an upgrade. Only nodes connected at the moment or seen within the last 96 hours are advertised to other nodes. Nodes not seen for 30 days are removed from storage, except the configured seed peers. diff --git a/peers/registry.go b/peers/registry.go index 0bf79b4..55ec82a 100644 --- a/peers/registry.go +++ b/peers/registry.go @@ -378,6 +378,14 @@ func (r *Registry) PruneStalePeers() (int, error) { threshold := now.Add(-staleAge) stale := make([]netip.Addr, 0) for _, p := range peers { + // Migrate legacy records even when they are exempt from pruning. + if p.LastSeen.IsZero() { + p.LastSeen = now + if putErr := r.storage.putPeer(p); putErr != nil { + return 0, fmt.Errorf("failed to prune stale peers: %w", putErr) + } + continue + } addr := p.AddressPort.Addr() if _, ok := r.seeds[addr]; ok { continue @@ -388,13 +396,6 @@ func (r *Registry) PruneStalePeers() (int, error) { if _, ok := r.pending[addr]; ok { continue } - if p.LastSeen.IsZero() { - p.LastSeen = now - if putErr := r.storage.putPeer(p); putErr != nil { - return 0, fmt.Errorf("failed to prune stale peers: %w", putErr) - } - continue - } if p.LastSeen.Before(threshold) { stale = append(stale, addr) } diff --git a/peers/registry_internal_test.go b/peers/registry_internal_test.go index 2e63b89..e5ec9aa 100644 --- a/peers/registry_internal_test.go +++ b/peers/registry_internal_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + "github.com/fxamacker/cbor/v2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/wavesplatform/gowaves/pkg/proto" @@ -171,3 +172,53 @@ func TestPruneStalePeersRetainsSeeds(t *testing.T) { require.NoError(t, err) require.Equal(t, []netip.AddrPort{seed}, available) } + +func TestPruneStalePeersBackfillsLegacyRecords(t *testing.T) { + for _, kind := range []string{"ordinary", "seed", "connected", "pending"} { + t.Run(kind, func(t *testing.T) { + r := newTestRegistry(t) + ap := netip.MustParseAddrPort("1.1.1.1:6868") + nextAttempt := time.Now().Add(time.Hour).Round(time.Second) + // Previous versions persisted no LastSeen field (CBOR key 7). + data, err := cbor.Marshal(map[uint64]any{ + 0: ap.Port(), 3: "1.5.0", 4: PeerConnected, 5: nextAttempt, + }) + require.NoError(t, err) + k := key{addr: ap.Addr()} + require.NoError(t, r.storage.db.Put(k.bytes(), data, nil)) + switch kind { + case "seed": + require.Zero(t, r.AppendSeedAddresses([]*net.TCPAddr{net.TCPAddrFromAddrPort(ap)})) + case "connected": + r.connections[ap.Addr()] = nil + case "pending": + r.pending[ap.Addr()] = struct{}{} + } + + before := time.Now().Round(time.Second) + removed, err := r.PruneStalePeers() + require.NoError(t, err) + require.Zero(t, removed) + p, err := r.Peer(ap.Addr()) + require.NoError(t, err) + require.False(t, p.LastSeen.Before(before)) + require.False(t, p.LastSeen.After(time.Now().Round(time.Second))) + require.Equal(t, PeerConnected, p.State) + require.Equal(t, nextAttempt, p.NextAttempt) + + active, err := r.ActivePeers() + require.NoError(t, err) + require.Equal(t, []string{ap.String()}, addresses(active)) + + // Subsequent pruning must preserve an existing timestamp. + p.LastSeen = before.Add(-time.Hour) + require.NoError(t, r.storage.putPeer(p)) + removed, err = r.PruneStalePeers() + require.NoError(t, err) + require.Zero(t, removed) + retained, err := r.Peer(ap.Addr()) + require.NoError(t, err) + require.Equal(t, p.LastSeen, retained.LastSeen) + }) + } +}