diff --git a/contribs/gnobro/go.mod b/contribs/gnobro/go.mod index 436e7968553..c7882cbe11d 100644 --- a/contribs/gnobro/go.mod +++ b/contribs/gnobro/go.mod @@ -1,8 +1,6 @@ module github.com/gnolang/gno/contribs/gnobro -go 1.23.6 - -toolchain go1.23.8 +go 1.24.0 require ( github.com/charmbracelet/bubbles v0.19.0 @@ -22,6 +20,7 @@ require ( ) require ( + connectrpc.com/connect v1.18.1 // indirect dario.cat/mergo v1.0.1 // indirect github.com/DataDog/zstd v1.4.5 // indirect github.com/alecthomas/chroma/v2 v2.15.0 // indirect diff --git a/contribs/gnobro/go.sum b/contribs/gnobro/go.sum index f1a2876ae25..27f1e764de1 100644 --- a/contribs/gnobro/go.sum +++ b/contribs/gnobro/go.sum @@ -1,3 +1,5 @@ +connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw= +connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= diff --git a/contribs/gnodev/go.mod b/contribs/gnodev/go.mod index c1850d1ff7b..93fe56b7bed 100644 --- a/contribs/gnodev/go.mod +++ b/contribs/gnodev/go.mod @@ -1,6 +1,6 @@ module github.com/gnolang/gno/contribs/gnodev -go 1.23.6 +go 1.24.0 replace github.com/gnolang/gno => ../.. @@ -18,6 +18,7 @@ require ( ) require ( + connectrpc.com/connect v1.18.1 // indirect dario.cat/mergo v1.0.1 // indirect github.com/DataDog/zstd v1.4.5 // indirect github.com/alecthomas/chroma/v2 v2.15.0 // indirect diff --git a/contribs/gnodev/go.sum b/contribs/gnodev/go.sum index 7a983abcfab..97751f4e6c2 100644 --- a/contribs/gnodev/go.sum +++ b/contribs/gnodev/go.sum @@ -1,3 +1,5 @@ +connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw= +connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= diff --git a/contribs/gnogenesis/go.mod b/contribs/gnogenesis/go.mod index 35560ca4ac9..1cca5815c03 100644 --- a/contribs/gnogenesis/go.mod +++ b/contribs/gnogenesis/go.mod @@ -8,6 +8,7 @@ require ( ) require ( + connectrpc.com/connect v1.18.1 // indirect dario.cat/mergo v1.0.1 // indirect github.com/DataDog/zstd v1.4.5 // indirect github.com/beorn7/perks v1.0.1 // indirect diff --git a/contribs/gnogenesis/go.sum b/contribs/gnogenesis/go.sum index 4a62c7e0277..f550e42938e 100644 --- a/contribs/gnogenesis/go.sum +++ b/contribs/gnogenesis/go.sum @@ -1,3 +1,5 @@ +connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw= +connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= diff --git a/contribs/gnohealth/go.sum b/contribs/gnohealth/go.sum index abe1d921e72..2eb2d6dd17a 100644 --- a/contribs/gnohealth/go.sum +++ b/contribs/gnohealth/go.sum @@ -1,3 +1,5 @@ +connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw= +connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/DataDog/zstd v1.5.7 h1:ybO8RBeh29qrxIhCA9E8gKY6xfONU9T6G6aP9DTKfLE= diff --git a/contribs/gnokeykc/go.sum b/contribs/gnokeykc/go.sum index cc01ad5cf47..c04d9c32ac8 100644 --- a/contribs/gnokeykc/go.sum +++ b/contribs/gnokeykc/go.sum @@ -1,3 +1,5 @@ +connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw= +connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/DataDog/zstd v1.5.7 h1:ybO8RBeh29qrxIhCA9E8gKY6xfONU9T6G6aP9DTKfLE= diff --git a/contribs/gnomigrate/go.mod b/contribs/gnomigrate/go.mod index 2a7b6bfc91a..090db6bb771 100644 --- a/contribs/gnomigrate/go.mod +++ b/contribs/gnomigrate/go.mod @@ -10,6 +10,7 @@ require ( replace github.com/gnolang/gno => ../.. require ( + connectrpc.com/connect v1.18.1 // indirect dario.cat/mergo v1.0.1 // indirect github.com/DataDog/zstd v1.4.5 // indirect github.com/beorn7/perks v1.0.1 // indirect diff --git a/contribs/gnomigrate/go.sum b/contribs/gnomigrate/go.sum index ef729bdf524..e4bd0f3690a 100644 --- a/contribs/gnomigrate/go.sum +++ b/contribs/gnomigrate/go.sum @@ -1,3 +1,5 @@ +connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw= +connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= diff --git a/contribs/tm2backup/.gitignore b/contribs/tm2backup/.gitignore new file mode 100644 index 00000000000..e2a1939bf60 --- /dev/null +++ b/contribs/tm2backup/.gitignore @@ -0,0 +1 @@ +blocks-backup* \ No newline at end of file diff --git a/contribs/tm2backup/Makefile b/contribs/tm2backup/Makefile new file mode 100644 index 00000000000..155fc997012 --- /dev/null +++ b/contribs/tm2backup/Makefile @@ -0,0 +1,18 @@ +rundep := go run -modfile ../../misc/devdeps/go.mod +golangci_lint := $(rundep) github.com/golangci/golangci-lint/cmd/golangci-lint + + +.PHONY: install +install: + go install . + +.PHONY: build +build: + go build -o build/gnomigrate . + +lint: + $(golangci_lint) --config ../../.github/golangci.yml run ./... + +test: + go test $(GOTEST_FLAGS) -v ./... + diff --git a/contribs/tm2backup/go.mod b/contribs/tm2backup/go.mod new file mode 100644 index 00000000000..d45b99b5eec --- /dev/null +++ b/contribs/tm2backup/go.mod @@ -0,0 +1,37 @@ +module github.com/gnolang/gno/contribs/tm2backup + +go 1.23.0 + +toolchain go1.24.0 + +replace github.com/gnolang/gno => ../.. + +require ( + connectrpc.com/connect v1.18.1 + github.com/gnolang/gno v0.0.0-00010101000000-000000000000 + github.com/stretchr/testify v1.10.0 + go.uber.org/zap v1.27.0 +) + +require ( + github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect + github.com/btcsuite/btcd/btcutil v1.1.6 // indirect + github.com/cosmos/gogoproto v1.7.0 // indirect + github.com/cosmos/ics23/go v0.11.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect + github.com/gofrs/flock v0.12.1 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/klauspost/compress v1.18.0 // indirect + github.com/peterbourgon/ff/v3 v3.4.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/crypto v0.40.0 // indirect + golang.org/x/net v0.42.0 // indirect + golang.org/x/sys v0.34.0 // indirect + golang.org/x/term v0.33.0 // indirect + golang.org/x/text v0.28.0 // indirect + google.golang.org/protobuf v1.36.6 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/contribs/tm2backup/go.sum b/contribs/tm2backup/go.sum new file mode 100644 index 00000000000..db5864243b3 --- /dev/null +++ b/contribs/tm2backup/go.sum @@ -0,0 +1,164 @@ +connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw= +connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= +github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= +github.com/btcsuite/btcd v0.24.2 h1:aLmxPguqxza+4ag8R1I2nnJjSu2iFn/kqtHTIImswcY= +github.com/btcsuite/btcd v0.24.2/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg= +github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= +github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= +github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= +github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= +github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= +github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= +github.com/btcsuite/btcd/btcutil v1.1.6 h1:zFL2+c3Lb9gEgqKNzowKUPQNb8jV7v5Oaodi/AYFd6c= +github.com/btcsuite/btcd/btcutil v1.1.6/go.mod h1:9dFymx8HpuLqBnsPELrImQeTQfKBQqzqGbbV3jK55aE= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +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/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/cosmos/gogoproto v1.7.0 h1:79USr0oyXAbxg3rspGh/m4SWNyoz/GLaAh0QlCe2fro= +github.com/cosmos/gogoproto v1.7.0/go.mod h1:yWChEv5IUEYURQasfyBW5ffkMHR/90hiHgbNgrtp4j0= +github.com/cosmos/ics23/go v0.11.0 h1:jk5skjT0TqX5e5QJbEnwXIS2yI2vnmLOgpQPeM5RtnU= +github.com/cosmos/ics23/go v0.11.0/go.mod h1:A8OjxPE67hHST4Icw94hOxxFEJMBG031xIGF/JHNIY0= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= +github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= +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= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +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/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/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/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/peterbourgon/ff/v3 v3.4.0 h1:QBvM/rizZM1cB0p0lGMdmR7HxZeI/ZrBWB4DqLkMUBc= +github.com/peterbourgon/ff/v3 v3.4.0/go.mod h1:zjJVUhx+twciwfDl0zBcFzl4dW8axCRyXE/eKY9RztQ= +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/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +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.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +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.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= +golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= +golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +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.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= +golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= +golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= +golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= +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.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +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.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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= diff --git a/contribs/tm2backup/main.go b/contribs/tm2backup/main.go new file mode 100644 index 00000000000..88eba76c603 --- /dev/null +++ b/contribs/tm2backup/main.go @@ -0,0 +1,111 @@ +package main + +import ( + "context" + "flag" + "fmt" + "net/http" + "os" + + "connectrpc.com/connect" + "github.com/gnolang/gno/tm2/pkg/bft/backup/backuppb" + "github.com/gnolang/gno/tm2/pkg/bft/backup/backuppb/backuppbconnect" + "github.com/gnolang/gno/tm2/pkg/bft/backup/v1" + "github.com/gnolang/gno/tm2/pkg/commands" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +func main() { + cmd := newRootCmd(commands.NewDefaultIO()) + + cmd.Execute(context.Background(), os.Args[1:]) +} + +func newRootCmd(io commands.IO) *commands.Command { + cfg := &backupCfg{} + + cmd := commands.NewCommand( + commands.Metadata{ + ShortUsage: "[flags]", + ShortHelp: "efficiently backup tm2 blocks", + LongHelp: "Efficiently backup tendermint2 blocks", + }, + cfg, + func(ctx context.Context, _ []string) error { + return execBackup(ctx, cfg, io) + }, + ) + + return cmd +} + +type backupCfg struct { + remote string + outDir string + startHeight int64 + endHeight int64 +} + +func (c *backupCfg) RegisterFlags(fs *flag.FlagSet) { + fs.StringVar( + &c.remote, + "remote", + "http://localhost:4242", + "Backup service remote.", + ) + fs.StringVar( + &c.outDir, + "o", + "blocks-backup", + "Output directory.", + ) + fs.Int64Var( + &c.startHeight, + "start", + 0, + fmt.Sprintf("Start height. Will be aligned at a multiple of %d blocks. This option can't be used when resuming from an existing output directory.", backup.ChunkSize), + ) + fs.Int64Var( + &c.endHeight, + "end", + 0, + "End height, inclusive. Use 0 for latest height.", + ) +} + +func execBackup(ctx context.Context, c *backupCfg, io commands.IO) (resErr error) { + config := zap.NewDevelopmentConfig() + config.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder + core := zapcore.NewCore(zapcore.NewConsoleEncoder(config.EncoderConfig), zapcore.AddSync(io.Out()), config.Level) + logger := zap.New(core) + + return backup.WithWriter(c.outDir, c.startHeight, c.endHeight, logger, func(startHeight int64, write func(bytes []byte) error) error { + client := backuppbconnect.NewBackupServiceClient( + http.DefaultClient, + c.remote, + connect.WithGRPC(), + ) + res, err := client.StreamBlocks( + ctx, + connect.NewRequest(&backuppb.StreamBlocksRequest{ + StartHeight: startHeight, + EndHeight: c.endHeight, + }), + ) + if err != nil { + return fmt.Errorf("open blocks stream: %w", err) + } + + for { + ok := res.Receive() + if !ok { + return res.Err() + } + + if err := write(res.Msg().Data); err != nil { + return err + } + } + }) +} diff --git a/contribs/tm2backup/main_test.go b/contribs/tm2backup/main_test.go new file mode 100644 index 00000000000..b5d89e75a9a --- /dev/null +++ b/contribs/tm2backup/main_test.go @@ -0,0 +1,51 @@ +package main + +import ( + "context" + "net/http/httptest" + "os" + "testing" + + "github.com/gnolang/gno/tm2/pkg/bft/backup" + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/stretchr/testify/require" +) + +func TestBackup(t *testing.T) { + store := &mockBlockStore{height: 5, blocks: map[int64]*types.Block{ + 1: {Header: types.Header{Height: 1}}, + 2: {Header: types.Header{Height: 2}}, + 3: {Header: types.Header{Height: 3}}, + 4: {Header: types.Header{Height: 4}}, + 5: {Header: types.Header{Height: 5}}, + }} + + mux := backup.NewMux(store) + srv := httptest.NewServer(mux) + defer srv.Close() + + io := commands.NewTestIO() + io.SetOut(os.Stdout) + io.SetErr(os.Stderr) + + err := newRootCmd(io).ParseAndRun(context.Background(), []string{ + "--remote", srv.URL, + }) + require.NoError(t, err) +} + +type mockBlockStore struct { + height int64 + blocks map[int64]*types.Block +} + +// Height implements blockStore. +func (m *mockBlockStore) Height() int64 { + return m.height +} + +// LoadBlock implements blockStore. +func (m *mockBlockStore) LoadBlock(height int64) *types.Block { + return m.blocks[height] +} diff --git a/contribs/tx-archive/go.mod b/contribs/tx-archive/go.mod index 7fadbd287f4..e85d68868d7 100644 --- a/contribs/tx-archive/go.mod +++ b/contribs/tx-archive/go.mod @@ -13,6 +13,7 @@ require ( ) require ( + connectrpc.com/connect v1.18.1 // indirect dario.cat/mergo v1.0.2 // indirect github.com/DataDog/zstd v1.4.5 // indirect github.com/beorn7/perks v1.0.1 // indirect diff --git a/contribs/tx-archive/go.sum b/contribs/tx-archive/go.sum index 5a19e44339e..a714740f1dc 100644 --- a/contribs/tx-archive/go.sum +++ b/contribs/tx-archive/go.sum @@ -1,3 +1,5 @@ +connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw= +connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= diff --git a/gno.land/cmd/gnoland/restore.go b/gno.land/cmd/gnoland/restore.go new file mode 100644 index 00000000000..e7516507367 --- /dev/null +++ b/gno.land/cmd/gnoland/restore.go @@ -0,0 +1,86 @@ +package main + +import ( + "context" + "flag" + "fmt" + + "github.com/gnolang/gno/tm2/pkg/bft/backup/v1" + "github.com/gnolang/gno/tm2/pkg/commands" +) + +type restoreCfg struct { + nodeCfg + backupDir string + endHeight int64 + skipVerification bool +} + +func newRestoreCmd(io commands.IO) *commands.Command { + cfg := &restoreCfg{} + + return commands.NewCommand( + commands.Metadata{ + Name: "restore", + ShortUsage: "restore [flags]", + ShortHelp: "restore the Gnoland blockchain node", + LongHelp: "Restores the Gnoland blockchain node, with accompanying setup", + }, + cfg, + func(ctx context.Context, _ []string) error { + return execRestore(ctx, cfg, io) + }, + ) +} + +func (c *restoreCfg) RegisterFlags(fs *flag.FlagSet) { + c.nodeCfg.RegisterFlags(fs) + + fs.StringVar( + &c.backupDir, + "backup-dir", + "blocks-backup", + "directory where the backup files are", + ) + + fs.Int64Var( + &c.endHeight, + "end-height", + 0, + "height at which the restore process should stop", + ) + + fs.BoolVar( + &c.skipVerification, + "skip-verification", + false, + "skip commit verification of the backup files", + ) +} + +func execRestore(ctx context.Context, c *restoreCfg, io commands.IO) error { + gnoNode, err := createNode(ctx, &c.nodeCfg, io) + if err != nil { + return err + } + defer func() { + if closeErr := gnoNode.Config().LocalApp.Close(); closeErr != nil { + io.ErrPrintln("unable to close gnoland application:", closeErr) + } + }() + + // need block n+1 to commit block n + endHeight := c.endHeight + if endHeight != 0 { + endHeight += 1 + } + + startHeight := gnoNode.BlockStore().Height() + 1 + if c.endHeight != 0 && c.endHeight < startHeight { + return fmt.Errorf("invalid input: requested end height (#%d) is smaller than next chain height (#%d)", c.endHeight, startHeight) + } + + return backup.WithReader(c.backupDir, startHeight, endHeight, func(reader backup.Reader) error { + return gnoNode.Restore(ctx, reader, c.skipVerification) + }) +} diff --git a/gno.land/cmd/gnoland/restore_test.go b/gno.land/cmd/gnoland/restore_test.go new file mode 100644 index 00000000000..6935e1d0536 --- /dev/null +++ b/gno.land/cmd/gnoland/restore_test.go @@ -0,0 +1,78 @@ +package main + +import ( + "context" + "flag" + "os" + "path/filepath" + "testing" + "time" + + "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/bft/backup/v1" + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/stretchr/testify/require" + "go.uber.org/zap" +) + +func TestRestore(t *testing.T) { + backupDir := t.TempDir() + generateBackup(t, backupDir, 2) + + restoreDir := t.TempDir() + io := commands.NewTestIO() + io.SetOut(os.Stdout) + io.SetErr(os.Stderr) + err := newRestoreCmd(io).ParseAndRun(context.Background(), []string{ + "--data-dir", filepath.Join(restoreDir, "chain-data"), + "--backup-dir", backupDir, + "--genesis", filepath.Join(backupDir, "genesis.json"), + "--skip-genesis-sig-verification", "true", + "--lazy", + }) + require.NoError(t, err) +} + +func generateBackup(t *testing.T, backupDir string, height int64) { + t.Helper() + + // XXX: consider moving this into a gen command and commit a golden backup + + nodeDir := t.TempDir() + + io := commands.NewTestIO() + io.SetOut(os.Stdout) + io.SetErr(os.Stderr) + + cfg := &nodeCfg{} + fs := flag.NewFlagSet("", flag.PanicOnError) + cfg.RegisterFlags(fs) + require.NoError(t, fs.Parse([]string{ + "--lazy", + "--data-dir", nodeDir, + "--genesis", filepath.Join(backupDir, "genesis.json"), + "--skip-genesis-sig-verification", "true", + })) + + node, err := createNode(context.Background(), cfg, io) + require.NoError(t, err) + t.Cleanup(func() { + require.NoError(t, node.Config().LocalApp.Close()) + }) + + require.NoError(t, node.Start()) + for node.BlockStore().Height() < height { + time.Sleep(1 * time.Second) + } + require.NoError(t, node.Stop()) + + err = backup.WithWriter(backupDir, 0, height, zap.NewNop(), func(startHeight int64, write backup.Writer) error { + for i := startHeight; i <= height; i++ { + data, err := amino.Marshal(node.BlockStore().LoadBlock(i)) + require.NoError(t, err) + require.NoError(t, write(data)) + } + return nil + }) + require.NoError(t, err) +} diff --git a/gno.land/cmd/gnoland/root.go b/gno.land/cmd/gnoland/root.go index 8f84e7899b2..036a0440f88 100644 --- a/gno.land/cmd/gnoland/root.go +++ b/gno.land/cmd/gnoland/root.go @@ -38,6 +38,7 @@ func newRootCmd(io commands.IO) *commands.Command { newStartCmd(io), newSecretsCmd(io), newConfigCmd(io), + newRestoreCmd(io), ) return cmd diff --git a/gno.land/cmd/gnoland/start.go b/gno.land/cmd/gnoland/start.go index 9f256a5cc79..b554d80642b 100644 --- a/gno.land/cmd/gnoland/start.go +++ b/gno.land/cmd/gnoland/start.go @@ -22,9 +22,9 @@ import ( "github.com/gnolang/gno/tm2/pkg/events" osm "github.com/gnolang/gno/tm2/pkg/os" p2pTypes "github.com/gnolang/gno/tm2/pkg/p2p/types" - "github.com/gnolang/gno/tm2/pkg/std" "github.com/gnolang/gno/tm2/pkg/telemetry" + "go.uber.org/zap/zapcore" ) @@ -43,7 +43,7 @@ var startGraphic = strings.ReplaceAll(` // Keep in sync with contribs/gnogenesis/internal/txs/txs_add_packages.go var genesisDeployFee = std.NewFee(50000, std.MustParseCoin(ugnot.ValueString(1))) -type startCfg struct { +type nodeCfg struct { gnoRootDir string // TODO: remove as part of https://github.com/gnolang/gno/issues/1952 skipFailingGenesisTxs bool // TODO: remove as part of https://github.com/gnolang/gno/issues/1952 skipGenesisSigVerification bool // TODO: remove as part of https://github.com/gnolang/gno/issues/1952 @@ -60,7 +60,7 @@ type startCfg struct { } func newStartCmd(io commands.IO) *commands.Command { - cfg := &startCfg{} + cfg := &nodeCfg{} return commands.NewCommand( commands.Metadata{ @@ -76,7 +76,7 @@ func newStartCmd(io commands.IO) *commands.Command { ) } -func (c *startCfg) RegisterFlags(fs *flag.FlagSet) { +func (c *nodeCfg) RegisterFlags(fs *flag.FlagSet) { gnoroot := gnoenv.RootDir() defaultGenesisBalancesFile := filepath.Join(gnoroot, "gno.land", "genesis", "genesis_balances.txt") @@ -165,23 +165,54 @@ func (c *startCfg) RegisterFlags(fs *flag.FlagSet) { ) } -func execStart(ctx context.Context, c *startCfg, io commands.IO) error { +func execStart(ctx context.Context, c *nodeCfg, io commands.IO) error { + gnoNode, err := createNode(ctx, c, io) + if err != nil { + return err + } + + // Start the node (async) + if err := gnoNode.Start(); err != nil { + return fmt.Errorf("unable to start the Gnoland node, %w", err) + } + + // Wait for the exit signal + <-ctx.Done() + + if !gnoNode.IsRunning() { + return nil + } + + // Gracefully stop the gno node + if err := gnoNode.Stop(); err != nil { + return fmt.Errorf("unable to gracefully stop the Gnoland node, %w", err) + } + + // Gracefully stop the app + if err = gnoNode.Config().LocalApp.Close(); err != nil { + return fmt.Errorf("unable to gracefully close the Gnoland application: %w", err) + } + + return nil +} + +func createNode(ctx context.Context, c *nodeCfg, io commands.IO) (*node.Node, error) { // Get the absolute path to the node's data directory nodeDir, err := filepath.Abs(c.dataDir) if err != nil { - return fmt.Errorf("unable to get absolute path for data directory, %w", err) + return nil, fmt.Errorf("unable to get absolute path for data directory, %w", err) } // Get the absolute path to the node's genesis.json genesisPath, err := filepath.Abs(c.genesisFile) if err != nil { - return fmt.Errorf("unable to get absolute path for the genesis.json, %w", err) + return nil, fmt.Errorf("unable to get absolute path for the genesis.json, %w", err) } // Initialize the logger zapLogger, err := log.InitializeZapLogger(io.Out(), c.logLevel, c.logFormat) if err != nil { - return fmt.Errorf("unable to initialize zap logger, %w", err) + return nil, fmt.Errorf("unable to initialize zap logger, %w", err) } defer func() { @@ -194,37 +225,37 @@ func execStart(ctx context.Context, c *startCfg, io commands.IO) error { if c.lazyInit { if err := lazyInitNodeDir(io, nodeDir); err != nil { - return fmt.Errorf("unable to lazy-init the node directory, %w", err) + return nil, fmt.Errorf("unable to lazy-init the node directory, %w", err) } } // Load the configuration cfg, err := config.LoadConfig(nodeDir) if err != nil { - return fmt.Errorf("%s, %w", tryConfigInit, err) + return nil, fmt.Errorf("%s, %w", tryConfigInit, err) } // Check if the genesis.json exists if !osm.FileExists(genesisPath) { if !c.lazyInit { - return errMissingGenesis + return nil, errMissingGenesis } // Get the node key for signer init nodeKey, err := p2pTypes.LoadOrMakeNodeKey(cfg.NodeKeyFile()) if err != nil { - return fmt.Errorf("unable to load or make node key, %w", err) + return nil, fmt.Errorf("unable to load or make node key, %w", err) } // Init the signer based on the config signer, err := privval.NewSignerFromConfig(ctx, cfg.Consensus.PrivValidator, nodeKey.PrivKey, logger) if err != nil { - return fmt.Errorf("unable to instantiate signer based on config: %w", err) + return nil, fmt.Errorf("unable to instantiate signer based on config: %w", err) } // Init a new genesis.json if err := lazyInitGenesis(io, c, genesisPath, signer); err != nil { - return fmt.Errorf("unable to initialize genesis.json, %w", err) + return nil, fmt.Errorf("unable to initialize genesis.json, %w", err) } // Close the signer @@ -233,7 +264,7 @@ func execStart(ctx context.Context, c *startCfg, io commands.IO) error { // Initialize telemetry if err := telemetry.Init(*cfg.Telemetry); err != nil { - return fmt.Errorf("unable to initialize telemetry, %w", err) + return nil, fmt.Errorf("unable to initialize telemetry, %w", err) } // Print the starting graphic @@ -256,38 +287,16 @@ func execStart(ctx context.Context, c *startCfg, io commands.IO) error { logger, ) if err != nil { - return fmt.Errorf("unable to create the Gnoland app, %w", err) + return nil, fmt.Errorf("unable to create the Gnoland app, %w", err) } // Create a default node, with the given setup gnoNode, err := node.DefaultNewNode(cfg, genesisPath, evsw, logger) if err != nil { - return fmt.Errorf("unable to create the Gnoland node, %w", err) + return nil, fmt.Errorf("unable to create the Gnoland node, %w", err) } - // Start the node (async) - if err := gnoNode.Start(); err != nil { - return fmt.Errorf("unable to start the Gnoland node, %w", err) - } - - // Wait for the exit signal - <-ctx.Done() - - if !gnoNode.IsRunning() { - return nil - } - - // Gracefully stop the gno node - if err := gnoNode.Stop(); err != nil { - return fmt.Errorf("unable to gracefully stop the Gnoland node, %w", err) - } - - // Gracefully stop the app - if err = cfg.LocalApp.Close(); err != nil { - return fmt.Errorf("unable to gracefully close the Gnoland application: %w", err) - } - - return nil + return gnoNode, err } // lazyInitNodeDir initializes new secrets, and a default configuration @@ -344,7 +353,7 @@ func lazyInitNodeDir(io commands.IO, nodeDir string) error { // lazyInitGenesis a new genesis.json file, with a single validator func lazyInitGenesis( io commands.IO, - c *startCfg, + c *nodeCfg, genesisPath string, signer gnoland.GenesisSigner, ) error { @@ -363,7 +372,7 @@ func lazyInitGenesis( return nil } -func generateGenesisFile(genesisFile string, signer gnoland.GenesisSigner, c *startCfg) error { +func generateGenesisFile(genesisFile string, signer gnoland.GenesisSigner, c *nodeCfg) error { var ( pubKey = signer.PubKey() // There is an active constraint for gno.land transactions: diff --git a/gno.land/cmd/gnoland/start_test.go b/gno.land/cmd/gnoland/start_test.go index 9f691c1b56a..bdd7d6d1259 100644 --- a/gno.land/cmd/gnoland/start_test.go +++ b/gno.land/cmd/gnoland/start_test.go @@ -3,13 +3,16 @@ package main import ( "bytes" "context" + "flag" "fmt" "os" "path/filepath" + "runtime" "strings" "testing" "time" + "github.com/gnolang/gno/tm2/pkg/bft/config" "github.com/gnolang/gno/tm2/pkg/bft/rpc/client" "github.com/gnolang/gno/tm2/pkg/commands" "github.com/stretchr/testify/assert" @@ -199,3 +202,109 @@ func TestStart_Lazy(t *testing.T) { }) } } + +func TestCreateNode(t *testing.T) { + tcs := []struct { + name string + errContains string + args []string + prepare func(t *testing.T, dataDir string) + }{ + { + name: "lazy", + args: []string{ + "--lazy", + "--skip-genesis-sig-verification", "true", + }, + }, + { + name: "err init logger", + errContains: "unable to initialize zap logger", + args: []string{ + "--log-level", "NOTEXIST", + }, + }, + { + name: "err no config", + errContains: "unable to load config", + }, + { + name: "err no genesis", + errContains: "missing genesis.json", + prepare: func(t *testing.T, dataDir string) { + t.Helper() + confDir := filepath.Join(dataDir, "gnoland-data", "config") + require.NoError(t, os.MkdirAll(confDir, 0o775)) + err := config.WriteConfigFile(filepath.Join(confDir, "config.toml"), config.DefaultConfig()) + require.NoError(t, err) + }, + }, + } + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + testDir := t.TempDir() + chtestdir(t, testDir) + + if tc.prepare != nil { + tc.prepare(t, testDir) + } + + cfg := &nodeCfg{} + + fset := flag.NewFlagSet("test", flag.PanicOnError) + cfg.RegisterFlags(fset) + + require.NoError(t, fset.Parse(tc.args)) + + io := commands.NewTestIO() + io.SetOut(os.Stdout) + io.SetErr(os.Stderr) + node, err := createNode(context.Background(), cfg, io) + if tc.errContains != "" { + require.ErrorContains(t, err, tc.errContains) + return + } + require.NoError(t, err) + t.Cleanup(func() { + require.NoError(t, node.Config().LocalApp.Close()) + }) + }) + } +} + +func chtestdir(t *testing.T, dir string) { + t.Helper() + + oldwd, err := os.Open(".") + if err != nil { + t.Fatal(err) + } + if err := os.Chdir(dir); err != nil { + t.Fatal(err) + } + // On POSIX platforms, PWD represents “an absolute pathname of the + // current working directory.” Since we are changing the working + // directory, we should also set or update PWD to reflect that. + switch runtime.GOOS { + case "windows", "plan9": + // Windows and Plan 9 do not use the PWD variable. + default: + if !filepath.IsAbs(dir) { + dir, err = os.Getwd() + if err != nil { + t.Fatal(err) + } + } + t.Setenv("PWD", dir) + } + t.Cleanup(func() { + err := oldwd.Chdir() + oldwd.Close() + if err != nil { + // It's not safe to continue with tests if we can't + // get back to the original working directory. Since + // we are holding a dirfd, this is highly unlikely. + panic("testing.Chdir: " + err.Error()) + } + }) +} diff --git a/gnovm/pkg/gnolang/string_methods.go b/gnovm/pkg/gnolang/string_methods.go index 45caf4f05fb..0892c4aa96d 100644 --- a/gnovm/pkg/gnolang/string_methods.go +++ b/gnovm/pkg/gnolang/string_methods.go @@ -46,10 +46,11 @@ const _Kind_name = "InvalidKindBoolKindStringKindIntKindInt8KindInt16KindInt32Ki var _Kind_index = [...]uint16{0, 11, 19, 29, 36, 44, 53, 62, 71, 79, 88, 98, 108, 118, 129, 140, 150, 160, 169, 178, 189, 199, 210, 223, 231, 239, 246, 254, 263, 275, 284, 295} func (i Kind) String() string { - if i >= Kind(len(_Kind_index)-1) { + idx := int(i) - 0 + if i < 0 || idx >= len(_Kind_index)-1 { return "Kind(" + strconv.FormatInt(int64(i), 10) + ")" } - return _Kind_name[_Kind_index[i]:_Kind_index[i+1]] + return _Kind_name[_Kind_index[idx]:_Kind_index[idx+1]] } func _() { // An "invalid array index" compiler error signifies that the constant values have changed. @@ -290,10 +291,11 @@ const _TransCtrl_name = "TRANS_CONTINUETRANS_SKIPTRANS_EXIT" var _TransCtrl_index = [...]uint8{0, 14, 24, 34} func (i TransCtrl) String() string { - if i >= TransCtrl(len(_TransCtrl_index)-1) { + idx := int(i) - 0 + if i < 0 || idx >= len(_TransCtrl_index)-1 { return "TransCtrl(" + strconv.FormatInt(int64(i), 10) + ")" } - return _TransCtrl_name[_TransCtrl_index[i]:_TransCtrl_index[i+1]] + return _TransCtrl_name[_TransCtrl_index[idx]:_TransCtrl_index[idx+1]] } func _() { // An "invalid array index" compiler error signifies that the constant values have changed. @@ -385,10 +387,11 @@ const _TransField_name = "TRANS_ROOTTRANS_BINARY_LEFTTRANS_BINARY_RIGHTTRANS_CAL var _TransField_index = [...]uint16{0, 10, 27, 45, 60, 74, 87, 104, 120, 133, 148, 164, 179, 191, 202, 220, 241, 254, 274, 293, 314, 332, 358, 376, 396, 416, 435, 454, 473, 492, 518, 538, 558, 579, 596, 615, 637, 653, 669, 685, 700, 716, 728, 742, 756, 770, 784, 797, 810, 823, 836, 849, 867, 881, 894, 909, 926, 942, 961, 978, 999, 1020, 1035, 1051, 1068, 1082, 1099, 1120, 1141, 1156, 1171, 1186, 1203, 1219, 1236, 1250, 1264, 1279, 1294, 1309} func (i TransField) String() string { - if i >= TransField(len(_TransField_index)-1) { + idx := int(i) - 0 + if i < 0 || idx >= len(_TransField_index)-1 { return "TransField(" + strconv.FormatInt(int64(i), 10) + ")" } - return _TransField_name[_TransField_index[i]:_TransField_index[i+1]] + return _TransField_name[_TransField_index[idx]:_TransField_index[idx+1]] } func _() { // An "invalid array index" compiler error signifies that the constant values have changed. @@ -508,8 +511,9 @@ const _Word_name = "ILLEGALNAMEINTFLOATIMAGCHARSTRINGADDSUBMULQUOREMBANDBORXORSH var _Word_index = [...]uint16{0, 7, 11, 14, 19, 23, 27, 33, 36, 39, 42, 45, 48, 52, 55, 58, 61, 64, 72, 82, 92, 102, 112, 122, 133, 143, 153, 163, 173, 188, 192, 195, 200, 203, 206, 209, 212, 215, 221, 224, 227, 230, 233, 239, 244, 248, 252, 257, 265, 272, 277, 281, 292, 295, 299, 301, 305, 307, 313, 322, 325, 332, 337, 343, 349, 355, 361, 365, 368} func (i Word) String() string { - if i < 0 || i >= Word(len(_Word_index)-1) { + idx := int(i) - 0 + if i < 0 || idx >= len(_Word_index)-1 { return "Word(" + strconv.FormatInt(int64(i), 10) + ")" } - return _Word_name[_Word_index[i]:_Word_index[i+1]] + return _Word_name[_Word_index[idx]:_Word_index[idx+1]] } diff --git a/go.mod b/go.mod index 525ba2348d7..e98e1b4bb5e 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.23.0 toolchain go1.24.1 require ( + connectrpc.com/connect v1.18.1 dario.cat/mergo v1.0.1 github.com/alecthomas/chroma/v2 v2.15.0 github.com/bendory/conway-hebrew-calendar v0.0.0-20210829020739-dcc34210ce9b @@ -23,6 +24,7 @@ require ( github.com/google/gofuzz v1.2.0 github.com/gorilla/websocket v1.5.3 github.com/hashicorp/golang-lru/v2 v2.0.7 + github.com/klauspost/compress v1.16.0 github.com/libp2p/go-buffer-pool v0.1.0 github.com/pelletier/go-toml v1.9.5 github.com/peterbourgon/ff/v3 v3.4.0 @@ -79,7 +81,6 @@ require ( github.com/google/go-cmp v0.6.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect - github.com/klauspost/compress v1.16.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect diff --git a/go.sum b/go.sum index ab39e34afbb..6dc2ecb5ac1 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw= +connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= diff --git a/misc/autocounterd/go.sum b/misc/autocounterd/go.sum index c34609d099f..fa74fd69ca6 100644 --- a/misc/autocounterd/go.sum +++ b/misc/autocounterd/go.sum @@ -1,3 +1,5 @@ +connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw= +connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= diff --git a/misc/devdeps/deps.go b/misc/devdeps/deps.go index 10118414698..d23d97cc59d 100644 --- a/misc/devdeps/deps.go +++ b/misc/devdeps/deps.go @@ -32,4 +32,9 @@ import ( // required to generate mocks (see `make mocks`) _ "github.com/golang/mock/mockgen" + + // backup grpc + _ "connectrpc.com/connect/cmd/protoc-gen-connect-go" + _ "github.com/bufbuild/buf/cmd/buf" + _ "google.golang.org/protobuf/cmd/protoc-gen-go" ) diff --git a/misc/devdeps/go.mod b/misc/devdeps/go.mod index 4ee3d0e60e6..f27cd7018d6 100644 --- a/misc/devdeps/go.mod +++ b/misc/devdeps/go.mod @@ -1,35 +1,57 @@ module github.com/gnolang/gno/misc/devdeps -go 1.23.0 - -toolchain go1.23.2 +go 1.24.0 require ( github.com/campoy/embedmd v1.0.0 github.com/golang/mock v1.6.0 github.com/golangci/golangci-lint/v2 v2.3.1 // sync with github actions - golang.org/x/tools v0.35.0 - google.golang.org/protobuf v1.36.6 + golang.org/x/tools v0.38.0 + google.golang.org/protobuf v1.36.10 moul.io/testman v1.5.0 mvdan.cc/gofumpt v0.8.0 ) require google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 +require ( + connectrpc.com/connect v1.19.1 + github.com/bufbuild/buf v1.59.0 + golang.org/x/tools/cmd/godoc v0.1.0-deprecated +) + require ( 4d63.com/gocheckcompilerdirectives v1.3.0 // indirect 4d63.com/gochecknoglobals v0.2.2 // indirect + buf.build/gen/go/bufbuild/bufplugin/protocolbuffers/go v1.36.10-20250718181942-e35f9b667443.1 // indirect + buf.build/gen/go/bufbuild/protodescriptor/protocolbuffers/go v1.36.10-20250109164928-1da0de137947.1 // indirect + buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.10-20250912141014-52f32327d4b0.1 // indirect + buf.build/gen/go/bufbuild/registry/connectrpc/go v1.19.1-20250924144421-cb55f06efbd2.2 // indirect + buf.build/gen/go/bufbuild/registry/protocolbuffers/go v1.36.10-20250924144421-cb55f06efbd2.1 // indirect + buf.build/gen/go/pluginrpc/pluginrpc/protocolbuffers/go v1.36.10-20241007202033-cf42259fcbfc.1 // indirect + buf.build/go/app v0.1.0 // indirect + buf.build/go/bufplugin v0.9.0 // indirect + buf.build/go/bufprivateusage v0.1.0 // indirect + buf.build/go/interrupt v1.1.0 // indirect + buf.build/go/protovalidate v1.0.0 // indirect + buf.build/go/protoyaml v0.6.0 // indirect + buf.build/go/spdx v0.2.0 // indirect + buf.build/go/standard v0.1.0 // indirect + cel.dev/expr v0.24.0 // indirect codeberg.org/chavacava/garif v0.2.0 // indirect + connectrpc.com/otelconnect v0.8.0 // indirect github.com/4meepo/tagalign v1.4.3 // indirect github.com/Abirdcfly/dupword v0.1.6 // indirect github.com/AlwxSin/noinlineerr v1.0.5 // indirect github.com/Antonboom/errname v1.1.0 // indirect github.com/Antonboom/nilnil v1.1.0 // indirect github.com/Antonboom/testifylint v1.6.1 // indirect + github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect github.com/BurntSushi/toml v1.5.0 // indirect github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1 // indirect github.com/Masterminds/semver/v3 v3.4.0 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect github.com/OpenPeeDeeP/depguard/v2 v2.2.1 // indirect github.com/alecthomas/chroma/v2 v2.20.0 // indirect github.com/alecthomas/go-check-sumtype v0.3.1 // indirect @@ -38,6 +60,7 @@ require ( github.com/alfatraining/structtag v1.0.0 // indirect github.com/alingse/asasalint v0.0.11 // indirect github.com/alingse/nilnesserr v0.2.0 // indirect + github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/ashanbrown/forbidigo/v2 v2.1.0 // indirect github.com/ashanbrown/makezero/v2 v2.0.1 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect @@ -48,6 +71,8 @@ require ( github.com/bombsimon/wsl/v5 v5.1.1 // indirect github.com/breml/bidichk v0.3.3 // indirect github.com/breml/errchkjson v0.4.1 // indirect + github.com/bufbuild/protocompile v0.14.2-0.20251017200126-6da99d83224e // indirect + github.com/bufbuild/protoplugin v0.0.0-20250218205857-750e09ce93e1 // indirect github.com/butuzov/ireturn v0.4.0 // indirect github.com/butuzov/mirror v1.3.0 // indirect github.com/catenacyber/perfsprint v0.9.1 // indirect @@ -60,20 +85,35 @@ require ( github.com/charmbracelet/x/cellbuf v0.0.13 // indirect github.com/charmbracelet/x/term v0.2.1 // indirect github.com/ckaznocha/intrange v0.3.1 // indirect + github.com/containerd/errdefs v1.0.0 // indirect + github.com/containerd/errdefs/pkg v0.3.0 // indirect + github.com/containerd/stargz-snapshotter/estargz v0.17.0 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/curioswitch/go-reassign v0.3.0 // indirect github.com/daixiang0/gci v0.13.7 // indirect github.com/dave/dst v0.27.3 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/denis-tingaikin/go-header v0.5.0 // indirect + github.com/distribution/reference v0.6.0 // indirect github.com/dlclark/regexp2 v1.11.5 // indirect + github.com/docker/cli v28.5.1+incompatible // indirect + github.com/docker/distribution v2.8.3+incompatible // indirect + github.com/docker/docker v28.5.1+incompatible // indirect + github.com/docker/docker-credential-helpers v0.9.4 // indirect + github.com/docker/go-connections v0.6.0 // indirect + github.com/docker/go-units v0.5.0 // indirect github.com/ettle/strcase v0.2.0 // indirect github.com/fatih/color v1.18.0 // indirect github.com/fatih/structtag v1.2.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/firefart/nonamedreturns v1.0.6 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fzipp/gocyclo v0.6.0 // indirect github.com/ghostiam/protogetter v0.3.15 // indirect + github.com/go-chi/chi/v5 v5.2.3 // indirect github.com/go-critic/go-critic v0.13.0 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-toolsmith/astcast v1.1.0 // indirect github.com/go-toolsmith/astcopy v1.1.0 // indirect github.com/go-toolsmith/astequal v1.2.0 // indirect @@ -84,7 +124,7 @@ require ( github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/go-xmlfmt/xmlfmt v1.1.3 // indirect github.com/gobwas/glob v0.2.3 // indirect - github.com/gofrs/flock v0.12.1 // indirect + github.com/gofrs/flock v0.13.0 // indirect github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32 // indirect github.com/golangci/go-printf-func-name v0.1.0 // indirect github.com/golangci/gofmt v0.0.0-20250704145412-3e58ba0443c6 // indirect @@ -94,17 +134,22 @@ require ( github.com/golangci/revgrep v0.8.0 // indirect github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e // indirect github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e // indirect + github.com/google/cel-go v0.26.1 // indirect github.com/google/go-cmp v0.7.0 // indirect + github.com/google/go-containerregistry v0.20.6 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/gordonklaus/ineffassign v0.1.0 // indirect github.com/gostaticanalysis/analysisutil v0.7.1 // indirect github.com/gostaticanalysis/comment v1.5.0 // indirect github.com/gostaticanalysis/forcetypeassert v0.2.0 // indirect github.com/gostaticanalysis/nilerr v0.1.1 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect github.com/hashicorp/go-immutable-radix/v2 v2.1.0 // indirect github.com/hashicorp/go-version v1.7.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hexops/gotextdiff v1.0.3 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jdx/go-netrc v1.0.0 // indirect github.com/jgautheron/goconst v1.8.2 // indirect github.com/jingyugao/rowserrcheck v1.1.1 // indirect github.com/jjti/go-spancheck v0.6.5 // indirect @@ -112,6 +157,8 @@ require ( github.com/karamaru-alpha/copyloopvar v1.2.1 // indirect github.com/kisielk/errcheck v1.9.0 // indirect github.com/kkHAIKE/contextcheck v1.1.6 // indirect + github.com/klauspost/compress v1.18.0 // indirect + github.com/klauspost/pgzip v1.2.6 // indirect github.com/kulti/thelper v0.6.3 // indirect github.com/kunwardeep/paralleltest v1.0.14 // indirect github.com/lasiar/canonicalheader v1.1.2 // indirect @@ -133,15 +180,23 @@ require ( github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mgechev/revive v1.11.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/term v0.5.2 // indirect github.com/moricho/tparallel v0.3.2 // indirect + github.com/morikuni/aec v1.0.0 // indirect github.com/muesli/termenv v0.16.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/nakabonne/nestif v0.3.1 // indirect github.com/nishanths/exhaustive v0.12.0 // indirect github.com/nishanths/predeclared v0.2.2 // indirect github.com/nunnatsa/ginkgolinter v0.20.0 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.1 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/peterbourgon/ff/v3 v3.4.0 // indirect + github.com/petermattis/goid v0.0.0-20250904145737-900bdf8bb490 // indirect + github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/polyfloyd/go-errorlint v1.8.0 // indirect github.com/prometheus/client_golang v1.23.0 // indirect @@ -153,9 +208,13 @@ require ( github.com/quasilyte/gogrep v0.5.0 // indirect github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect + github.com/quic-go/qpack v0.5.1 // indirect + github.com/quic-go/quic-go v0.55.0 // indirect github.com/raeperd/recvcheck v0.2.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect + github.com/rs/cors v1.11.1 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryancurrah/gomodguard v1.4.1 // indirect github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect github.com/sagikazarmark/locafero v0.10.0 // indirect @@ -164,6 +223,8 @@ require ( github.com/sashamelentyev/interfacebloat v1.1.0 // indirect github.com/sashamelentyev/usestdlibvars v1.29.0 // indirect github.com/securego/gosec/v2 v2.22.7 // indirect + github.com/segmentio/asm v1.2.1 // indirect + github.com/segmentio/encoding v0.5.3 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/sivchari/containedctx v1.0.3 // indirect github.com/sonatard/noctx v0.4.0 // indirect @@ -171,16 +232,19 @@ require ( github.com/sourcegraph/go-diff v0.7.0 // indirect github.com/spf13/afero v1.14.0 // indirect github.com/spf13/cast v1.9.2 // indirect - github.com/spf13/cobra v1.9.1 // indirect - github.com/spf13/pflag v1.0.7 // indirect + github.com/spf13/cobra v1.10.1 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/spf13/viper v1.20.1 // indirect github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect github.com/stbenjam/no-sprintf-host-port v0.2.0 // indirect + github.com/stoewer/go-strcase v1.3.1 // indirect github.com/stretchr/objx v0.5.2 // indirect - github.com/stretchr/testify v1.10.0 // indirect + github.com/stretchr/testify v1.11.1 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/tdakkota/asciicheck v0.4.1 // indirect github.com/tetafro/godot v1.5.1 // indirect + github.com/tetratelabs/wazero v1.9.0 // indirect + github.com/tidwall/btree v1.8.1 // indirect github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67 // indirect github.com/timonwong/loggercheck v0.11.0 // indirect github.com/tomarrell/wrapcheck/v2 v2.11.0 // indirect @@ -189,6 +253,7 @@ require ( github.com/ultraware/whitespace v0.2.0 // indirect github.com/uudashr/gocognit v1.2.0 // indirect github.com/uudashr/iface v1.4.1 // indirect + github.com/vbatts/tar-split v0.12.1 // indirect github.com/xen0n/gosmopolitan v1.3.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/yagipy/maintidx v1.0.0 // indirect @@ -200,19 +265,37 @@ require ( go-simpler.org/sloglint v0.11.1 // indirect go.augendre.info/arangolint v0.2.0 // indirect go.augendre.info/fatcontext v0.8.0 // indirect + go.lsp.dev/jsonrpc2 v0.10.0 // indirect + go.lsp.dev/pkg v0.0.0-20210717090340-384b27a52fb2 // indirect + go.lsp.dev/protocol v0.12.0 // indirect + go.lsp.dev/uri v0.3.0 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect + go.opentelemetry.io/otel v1.38.0 // indirect + go.opentelemetry.io/otel/metric v1.38.0 // indirect + go.opentelemetry.io/otel/trace v1.38.0 // indirect go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 // indirect + golang.org/x/crypto v0.43.0 // indirect + golang.org/x/exp v0.0.0-20251009144603-d2f985daa21b // indirect golang.org/x/exp/typeparams v0.0.0-20250718183923-645b1fa84792 // indirect - golang.org/x/mod v0.26.0 // indirect - golang.org/x/sync v0.16.0 // indirect - golang.org/x/sys v0.34.0 // indirect - golang.org/x/text v0.27.0 // indirect + golang.org/x/mod v0.29.0 // indirect + golang.org/x/net v0.46.0 // indirect + golang.org/x/sync v0.17.0 // indirect + golang.org/x/sys v0.37.0 // indirect + golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8 // indirect + golang.org/x/term v0.36.0 // indirect + golang.org/x/text v0.30.0 // indirect + golang.org/x/tools/godoc v0.1.0-deprecated // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20251007200510-49b9836ed3ff // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20251007200510-49b9836ed3ff // indirect + google.golang.org/grpc v1.75.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect honnef.co/go/tools v0.6.1 // indirect moul.io/banner v1.0.1 // indirect moul.io/motd v1.0.0 // indirect moul.io/u v1.27.0 // indirect mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4 // indirect + pluginrpc.com/pluginrpc v0.5.0 // indirect ) diff --git a/misc/devdeps/go.sum b/misc/devdeps/go.sum index d7efee307c8..1f14564204a 100644 --- a/misc/devdeps/go.sum +++ b/misc/devdeps/go.sum @@ -2,8 +2,42 @@ 4d63.com/gocheckcompilerdirectives v1.3.0/go.mod h1:ofsJ4zx2QAuIP/NO/NAh1ig6R1Fb18/GI7RVMwz7kAY= 4d63.com/gochecknoglobals v0.2.2 h1:H1vdnwnMaZdQW/N+NrkT1SZMTBmcwHe9Vq8lJcYYTtU= 4d63.com/gochecknoglobals v0.2.2/go.mod h1:lLxwTQjL5eIesRbvnzIP3jZtG140FnTdz+AlMa+ogt0= +buf.build/gen/go/bufbuild/bufplugin/protocolbuffers/go v1.36.10-20250718181942-e35f9b667443.1 h1:FzJGrb8r7vir+P3zJ5Ebey8p54LYTYtQsrM/U35YO9Q= +buf.build/gen/go/bufbuild/bufplugin/protocolbuffers/go v1.36.10-20250718181942-e35f9b667443.1/go.mod h1:E6HwqUm4Ag7bXtg/tX7jHWO7CgpknbmeACgDax0icV0= +buf.build/gen/go/bufbuild/protodescriptor/protocolbuffers/go v1.36.10-20250109164928-1da0de137947.1 h1:9hkMnVoImDlY7rTlAWIWXdkGUKOjf3YlyZeSbYT29uA= +buf.build/gen/go/bufbuild/protodescriptor/protocolbuffers/go v1.36.10-20250109164928-1da0de137947.1/go.mod h1:/AouMCAeQ+kB7+RRFpdUlZe3503p18VoUNcU2AFqZXM= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.10-20250912141014-52f32327d4b0.1 h1:31on4W/yPcV4nZHL4+UCiCvLPsMqe/vJcNg8Rci0scc= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.10-20250912141014-52f32327d4b0.1/go.mod h1:fUl8CEN/6ZAMk6bP8ahBJPUJw7rbp+j4x+wCcYi2IG4= +buf.build/gen/go/bufbuild/registry/connectrpc/go v1.19.1-20250924144421-cb55f06efbd2.2 h1:hLW3Bta/Nplz6v//utPUmIqAWIIXFOMH0hWXJv9cWLQ= +buf.build/gen/go/bufbuild/registry/connectrpc/go v1.19.1-20250924144421-cb55f06efbd2.2/go.mod h1:YCi7xtUe0lVPR0IGmMHRgI1017VEayU6yS5VM1Sj1ms= +buf.build/gen/go/bufbuild/registry/protocolbuffers/go v1.36.10-20250924144421-cb55f06efbd2.1 h1:4ZcqdlWZF5tQUyHBeLyOwFjRwZ/93X/W5GfOZX9dJZo= +buf.build/gen/go/bufbuild/registry/protocolbuffers/go v1.36.10-20250924144421-cb55f06efbd2.1/go.mod h1:AaYXXeRvnOc151wEuupAmn58Mh9bccKce2kk3QKMIrQ= +buf.build/gen/go/pluginrpc/pluginrpc/protocolbuffers/go v1.36.10-20241007202033-cf42259fcbfc.1 h1:CzM0kZcoaIr8+R4i8QVorUNRM/CqMr87i3j+w2pdpCc= +buf.build/gen/go/pluginrpc/pluginrpc/protocolbuffers/go v1.36.10-20241007202033-cf42259fcbfc.1/go.mod h1:bG+Fa7tcA+4pW0JdOh4h7iKjleyZIKhfVzVS10qfrnk= +buf.build/go/app v0.1.0 h1:nlqD/h0rhIN73ZoiDElprrPiO2N6JV+RmNK34K29Ihg= +buf.build/go/app v0.1.0/go.mod h1:0XVOYemubVbxNXVY0DnsVgWeGkcbbAvjDa1fmhBC+Wo= +buf.build/go/bufplugin v0.9.0 h1:ktZJNP3If7ldcWVqh46XKeiYJVPxHQxCfjzVQDzZ/lo= +buf.build/go/bufplugin v0.9.0/go.mod h1:Z0CxA3sKQ6EPz/Os4kJJneeRO6CjPeidtP1ABh5jPPY= +buf.build/go/bufprivateusage v0.1.0 h1:SzCoCcmzS3zyXHEXHeSQhGI7OTkgtljoknLzsUz9Gg4= +buf.build/go/bufprivateusage v0.1.0/go.mod h1:GlCCJ3VVF7EqqU0CoRmo1FzAwwaKymEWSr+ty69xU5w= +buf.build/go/interrupt v1.1.0 h1:olBuhgv9Sav4/9pkSLoxgiOsZDgM5VhRhvRpn3DL0lE= +buf.build/go/interrupt v1.1.0/go.mod h1:ql56nXPG1oHlvZa6efNC7SKAQ/tUjS6z0mhJl0gyeRM= +buf.build/go/protovalidate v1.0.0 h1:IAG1etULddAy93fiBsFVhpj7es5zL53AfB/79CVGtyY= +buf.build/go/protovalidate v1.0.0/go.mod h1:KQmEUrcQuC99hAw+juzOEAmILScQiKBP1Oc36vvCLW8= +buf.build/go/protoyaml v0.6.0 h1:Nzz1lvcXF8YgNZXk+voPPwdU8FjDPTUV4ndNTXN0n2w= +buf.build/go/protoyaml v0.6.0/go.mod h1:RgUOsBu/GYKLDSIRgQXniXbNgFlGEZnQpRAUdLAFV2Q= +buf.build/go/spdx v0.2.0 h1:IItqM0/cMxvFJJumcBuP8NrsIzMs/UYjp/6WSpq8LTw= +buf.build/go/spdx v0.2.0/go.mod h1:bXdwQFem9Si3nsbNy8aJKGPoaPi5DKwdeEp5/ArZ6w8= +buf.build/go/standard v0.1.0 h1:g98T9IyvAl0vS3Pq8iVk6Cvj2ZiFvoUJRtfyGa0120U= +buf.build/go/standard v0.1.0/go.mod h1:PiqpHz/7ZFq+kqvYhc/SK3lxFIB9N/aiH2CFC2JHIQg= +cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY= +cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= codeberg.org/chavacava/garif v0.2.0 h1:F0tVjhYbuOCnvNcU3YSpO6b3Waw6Bimy4K0mM8y6MfY= codeberg.org/chavacava/garif v0.2.0/go.mod h1:P2BPbVbT4QcvLZrORc2T29szK3xEOlnl0GiPTJmEqBQ= +connectrpc.com/connect v1.19.1 h1:R5M57z05+90EfEvCY1b7hBxDVOUl45PrtXtAV2fOC14= +connectrpc.com/connect v1.19.1/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w= +connectrpc.com/otelconnect v0.8.0 h1:a4qrN4H8aEE2jAoCxheZYYfEjXMgVPyL9OzPQLBEFXU= +connectrpc.com/otelconnect v0.8.0/go.mod h1:AEkVLjCPXra+ObGFCOClcJkNjS7zPaQSqvO0lCyjfZc= github.com/4meepo/tagalign v1.4.3 h1:Bnu7jGWwbfpAie2vyl63Zup5KuRv21olsPIha53BJr8= github.com/4meepo/tagalign v1.4.3/go.mod h1:00WwRjiuSbrRJnSVeGWPLp2epS5Q/l4UEy0apLLS37c= github.com/Abirdcfly/dupword v0.1.6 h1:qeL6u0442RPRe3mcaLcbaCi2/Y/hOcdtw6DE9odjz9c= @@ -16,6 +50,8 @@ github.com/Antonboom/nilnil v1.1.0 h1:jGxJxjgYS3VUUtOTNk8Z1icwT5ESpLH/426fjmQG+n github.com/Antonboom/nilnil v1.1.0/go.mod h1:b7sAlogQjFa1wV8jUW3o4PMzDVFLbTux+xnQdvzdcIE= github.com/Antonboom/testifylint v1.6.1 h1:6ZSytkFWatT8mwZlmRCHkWz1gPi+q6UBSbieji2Gj/o= github.com/Antonboom/testifylint v1.6.1/go.mod h1:k+nEkathI2NFjKO6HvwmSrbzUcQ6FAnbZV+ZRrnXPLI= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= @@ -25,6 +61,8 @@ github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1 h1:Sz1JIXEcSfhz7fUi7xHnh github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1/go.mod h1:n/LSCXNuIYqVfBlVXyHfMQkZDdp1/mmxfSjADd3z1Zg= github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/OpenPeeDeeP/depguard/v2 v2.2.1 h1:vckeWVESWp6Qog7UZSARNqfu/cZqvki8zsuj3piCMx4= github.com/OpenPeeDeeP/depguard/v2 v2.2.1/go.mod h1:q4DKzC4UcVaAvcfd41CZh0PWpGgzrVxUYBlgKNGquUo= github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= @@ -45,6 +83,8 @@ github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQ github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I= github.com/alingse/nilnesserr v0.2.0 h1:raLem5KG7EFVb4UIDAXgrv3N2JIaffeKNtcEXkEWd/w= github.com/alingse/nilnesserr v0.2.0/go.mod h1:1xJPrXonEtX7wyTq8Dytns5P2hNzoWymVUIaKm4HNFg= +github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= +github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= github.com/ashanbrown/forbidigo/v2 v2.1.0 h1:NAxZrWqNUQiDz19FKScQ/xvwzmij6BiOw3S0+QUQ+Hs= github.com/ashanbrown/forbidigo/v2 v2.1.0/go.mod h1:0zZfdNAuZIL7rSComLGthgc/9/n2FqspBOH90xlCHdA= github.com/ashanbrown/makezero/v2 v2.0.1 h1:r8GtKetWOgoJ4sLyUx97UTwyt2dO7WkGFHizn/Lo8TY= @@ -57,6 +97,8 @@ github.com/bkielbasa/cyclop v1.2.3 h1:faIVMIGDIANuGPWH031CZJTi2ymOQBULs9H21HSMa5 github.com/bkielbasa/cyclop v1.2.3/go.mod h1:kHTwA9Q0uZqOADdupvcFJQtp/ksSnytRMe8ztxG8Fuo= github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ089M= github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k= +github.com/bmatcuk/doublestar/v4 v4.9.1 h1:X8jg9rRZmJd4yRy7ZeNDRnM+T3ZfHv15JiBJ/avrEXE= +github.com/bmatcuk/doublestar/v4 v4.9.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/bombsimon/wsl/v4 v4.7.0 h1:1Ilm9JBPRczjyUs6hvOPKvd7VL1Q++PL8M0SXBDf+jQ= github.com/bombsimon/wsl/v4 v4.7.0/go.mod h1:uV/+6BkffuzSAVYD+yGyld1AChO7/EuLrCF/8xTiapg= github.com/bombsimon/wsl/v5 v5.1.1 h1:cQg5KJf9FlctAH4cpL9vLKnziYknoCMCdqXl0wjl72Q= @@ -65,6 +107,12 @@ github.com/breml/bidichk v0.3.3 h1:WSM67ztRusf1sMoqH6/c4OBCUlRVTKq+CbSeo0R17sE= github.com/breml/bidichk v0.3.3/go.mod h1:ISbsut8OnjB367j5NseXEGGgO/th206dVa427kR8YTE= github.com/breml/errchkjson v0.4.1 h1:keFSS8D7A2T0haP9kzZTi7o26r7kE3vymjZNeNDRDwg= github.com/breml/errchkjson v0.4.1/go.mod h1:a23OvR6Qvcl7DG/Z4o0el6BRAjKnaReoPQFciAl9U3s= +github.com/bufbuild/buf v1.59.0 h1:Ytb/YpyKrC4aa30iFhKja00c+9zekNMhcp+fXvUPAbo= +github.com/bufbuild/buf v1.59.0/go.mod h1:KVVaGAOdsFWPyoRPPcJuu3Tq/O+/s+O5i+TynhRbJKI= +github.com/bufbuild/protocompile v0.14.2-0.20251017200126-6da99d83224e h1:k4BETn+kh6RM6d+fG80avRtVc/v3mzFx92I4XqYOgGQ= +github.com/bufbuild/protocompile v0.14.2-0.20251017200126-6da99d83224e/go.mod h1:HKN246DRQwavs64sr2xYmSL+RFOFxmLti+WGCZ2jh9U= +github.com/bufbuild/protoplugin v0.0.0-20250218205857-750e09ce93e1 h1:V1xulAoqLqVg44rY97xOR+mQpD2N+GzhMHVwJ030WEU= +github.com/bufbuild/protoplugin v0.0.0-20250218205857-750e09ce93e1/go.mod h1:c5D8gWRIZ2HLWO3gXYTtUfw/hbJyD8xikv2ooPxnklQ= github.com/butuzov/ireturn v0.4.0 h1:+s76bF/PfeKEdbG8b54aCocxXmi0wvYdOVsWxVO7n8E= github.com/butuzov/ireturn v0.4.0/go.mod h1:ghI0FrCmap8pDWZwfPisFD1vEc56VKH4NpQUxDHta70= github.com/butuzov/mirror v1.3.0 h1:HdWCXzmwlQHdVhwvsfBb2Au0r3HyINry3bDWLYXiKoc= @@ -75,6 +123,8 @@ github.com/catenacyber/perfsprint v0.9.1 h1:5LlTp4RwTooQjJCvGEFV6XksZvWE7wCOUvjD github.com/catenacyber/perfsprint v0.9.1/go.mod h1:q//VWC2fWbcdSLEY1R3l8n0zQCDPdE4IjZwyY1HMunM= github.com/ccojocar/zxcvbn-go v1.0.4 h1:FWnCIRMXPj43ukfX000kvBZvV6raSxakYr1nzyNrUcc= github.com/ccojocar/zxcvbn-go v1.0.4/go.mod h1:3GxGX+rHmueTUMvm5ium7irpyjmm7ikxYFOSJB21Das= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= 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/charithe/durationcheck v0.0.10 h1:wgw73BiocdBDQPik+zcEoBG/ob8uyBHf2iyoHGPf5w4= @@ -91,8 +141,20 @@ github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQ github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= github.com/ckaznocha/intrange v0.3.1 h1:j1onQyXvHUsPWujDH6WIjhyH26gkRt/txNlV7LspvJs= github.com/ckaznocha/intrange v0.3.1/go.mod h1:QVepyz1AkUoFQkpEqksSYpNpUo3c5W7nWh/s6SHIJJk= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/stargz-snapshotter/estargz v0.17.0 h1:+TyQIsR/zSFI1Rm31EQBwpAA1ovYgIKHy7kctL3sLcE= +github.com/containerd/stargz-snapshotter/estargz v0.17.0/go.mod h1:s06tWAiJcXQo9/8AReBCIo/QxcXFZ2n4qfsRnpl71SM= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= +github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/curioswitch/go-reassign v0.3.0 h1:dh3kpQHuADL3cobV/sSGETA8DOv457dwl+fbBAhrQPs= github.com/curioswitch/go-reassign v0.3.0/go.mod h1:nApPCCTtqLJN/s8HfItCcKV0jIPwluBOvZP+dsJGA88= github.com/daixiang0/gci v0.13.7 h1:+0bG5eK9vlI08J+J/NWGbWPTNiXPG4WhNLJOkSxWITQ= @@ -107,14 +169,30 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/denis-tingaikin/go-header v0.5.0 h1:SRdnP5ZKvcO9KKRP1KJrhFR3RrlGuD+42t4429eC9k8= github.com/denis-tingaikin/go-header v0.5.0/go.mod h1:mMenU5bWrok6Wl2UsZjy+1okegmwQ3UgWl4V1D8gjlY= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/docker/cli v28.5.1+incompatible h1:ESutzBALAD6qyCLqbQSEf1a/U8Ybms5agw59yGVc+yY= +github.com/docker/cli v28.5.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= +github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v28.5.1+incompatible h1:Bm8DchhSD2J6PsFzxC35TZo4TLGR2PdW/E69rU45NhM= +github.com/docker/docker v28.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.9.4 h1:76ItO69/AP/V4yT9V4uuuItG0B1N8hvt0T0c0NN/DzI= +github.com/docker/docker-credential-helpers v0.9.4/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= +github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= +github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q= github.com/ettle/strcase v0.2.0/go.mod h1:DajmHElDSaX76ITe3/VHVyMin4LWSJN5Z909Wp+ED1A= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/firefart/nonamedreturns v1.0.6 h1:vmiBcKV/3EqKY3ZiPxCINmpS431OcE1S47AQUwhrg8E= github.com/firefart/nonamedreturns v1.0.6/go.mod h1:R8NisJnSIpvPWheCq0mNRXJok6D8h7fagJTF8EMEwCo= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -125,10 +203,15 @@ github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo= github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= github.com/ghostiam/protogetter v0.3.15 h1:1KF5sXel0HE48zh1/vn0Loiw25A9ApyseLzQuif1mLY= github.com/ghostiam/protogetter v0.3.15/go.mod h1:WZ0nw9pfzsgxuRsPOFQomgDVSWtDLJRfQJEhsGbmQMA= +github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE= +github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= github.com/go-critic/go-critic v0.13.0 h1:kJzM7wzltQasSUXtYyTl6UaPVySO6GkaR1thFnJ6afY= github.com/go-critic/go-critic v0.13.0/go.mod h1:M/YeuJ3vOCQDnP2SU+ZhjgRzwzcBW87JqLpMJLrZDLI= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 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/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= @@ -158,8 +241,8 @@ github.com/go-xmlfmt/xmlfmt v1.1.3 h1:t8Ey3Uy7jDSEisW2K3somuMKIpzktkWptA0iFCnRUW github.com/go-xmlfmt/xmlfmt v1.1.3/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= -github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= +github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw= +github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= @@ -183,14 +266,21 @@ github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e h1:ai0EfmVYE2b github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e/go.mod h1:Vrn4B5oR9qRwM+f54koyeH3yzphlecwERs0el27Fr/s= github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e h1:gD6P7NEo7Eqtt0ssnqSJNNndxe69DOQ24A5h7+i3KpM= github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e/go.mod h1:h+wZwLjUTJnm/P2rwlbJdRPZXOzaT36/FwnPnY2inzc= +github.com/google/cel-go v0.26.1 h1:iPbVVEdkhTX++hpe3lzSk7D3G3QSYqLGoHOcEio+UXQ= +github.com/google/cel-go v0.26.1/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/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/go-containerregistry v0.20.6 h1:cvWX87UxxLgaH76b4hIvya6Dzz9qHB31qAwjAohdSTU= +github.com/google/go-containerregistry v0.20.6/go.mod h1:T0x8MuoAoKX/873bkeSfLD2FAkwCDf9/HZgsFJ02E2Y= github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a h1://KbezygeMJZCSHH+HgUZiTeSoiuFspbMg1ge+eFj18= github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA= +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/gordonklaus/ineffassign v0.1.0 h1:y2Gd/9I7MdY1oEIt+n+rowjBNDcLQq3RsH5hwJd0f9s= github.com/gordonklaus/ineffassign v0.1.0/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0= github.com/gostaticanalysis/analysisutil v0.7.1 h1:ZMCjoue3DtDWQ5WyU16YbjbQEQ3VuzwxALrpYd+HeKk= @@ -206,6 +296,8 @@ github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M= github.com/gostaticanalysis/testutil v0.5.0 h1:Dq4wT1DdTwTGCQQv3rl3IvD5Ld0E6HiY+3Zh0sUGqw8= github.com/gostaticanalysis/testutil v0.5.0/go.mod h1:OLQSbuM6zw2EvCcXTz1lVq5unyoNft372msDY0nY5Hs= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4= github.com/hashicorp/go-immutable-radix/v2 v2.1.0 h1:CUW5RYIcysz+D3B+l1mDeXrQ7fUvGGCwJfdASSzbrfo= github.com/hashicorp/go-immutable-radix/v2 v2.1.0/go.mod h1:hgdqLXA4f6NIjRVisM1TJ9aOJVNRqKZj+xDGF6m7PBw= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= @@ -219,8 +311,12 @@ github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUq github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jdx/go-netrc v1.0.0 h1:QbLMLyCZGj0NA8glAhxUpf1zDg6cxnWgMBbjq40W0gQ= +github.com/jdx/go-netrc v1.0.0/go.mod h1:Gh9eFQJnoTNIRHXl2j5bJXA1u84hQWJWgGh569zF3v8= github.com/jgautheron/goconst v1.8.2 h1:y0XF7X8CikZ93fSNT6WBTb/NElBu9IjaY7CCYQrCMX4= github.com/jgautheron/goconst v1.8.2/go.mod h1:A0oxgBCHy55NQn6sYpO7UdnA9p+h7cPtoOZUmvNIako= +github.com/jhump/protoreflect/v2 v2.0.0-beta.2 h1:qZU+rEZUOYTz1Bnhi3xbwn+VxdXkLVeEpAeZzVXLY88= +github.com/jhump/protoreflect/v2 v2.0.0-beta.2/go.mod h1:4tnOYkB/mq7QTyS3YKtVtNrJv4Psqout8HA1U+hZtgM= github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjzq7gFzUs= github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c= github.com/jjti/go-spancheck v0.6.5 h1:lmi7pKxa37oKYIMScialXUK6hP3iY5F1gu+mLBPgYB8= @@ -235,6 +331,10 @@ github.com/kisielk/errcheck v1.9.0/go.mod h1:kQxWMMVZgIkDq7U8xtG/n2juOjbLgZtedi0 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkHAIKE/contextcheck v1.1.6 h1:7HIyRcnyzxL9Lz06NGhiKvenXq7Zw6Q0UQu/ttjfJCE= github.com/kkHAIKE/contextcheck v1.1.6/go.mod h1:3dDbMRNBFaq8HFXWC1JyvDSPm43CmE6IuHam8Wr0rkg= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= +github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -287,8 +387,18 @@ github.com/mgechev/revive v1.11.0 h1:b/gLLpBE427o+Xmd8G58gSA+KtBwxWinH/A565Awh0w github.com/mgechev/revive v1.11.0/go.mod h1:tI0oLF/2uj+InHCBLrrqfTKfjtFTBCFFfG05auyzgdw= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= +github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= +github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= +github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= +github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= github.com/moricho/tparallel v0.3.2 h1:odr8aZVFA3NZrNybggMkYO3rgPRcqjeQUlBBFVxKHTI= github.com/moricho/tparallel v0.3.2/go.mod h1:OQ+K3b4Ln3l2TZveGCywybl68glfLEwFGqvnjok8b+U= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= @@ -305,6 +415,10 @@ github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y= github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= +github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= @@ -318,8 +432,14 @@ github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8 github.com/peterbourgon/ff/v3 v3.0.0/go.mod h1:UILIFjRH5a/ar8TjXYLTkIvSvekZqPm5Eb/qbGk6CT0= github.com/peterbourgon/ff/v3 v3.4.0 h1:QBvM/rizZM1cB0p0lGMdmR7HxZeI/ZrBWB4DqLkMUBc= github.com/peterbourgon/ff/v3 v3.4.0/go.mod h1:zjJVUhx+twciwfDl0zBcFzl4dW8axCRyXE/eKY9RztQ= +github.com/petermattis/goid v0.0.0-20250904145737-900bdf8bb490 h1:QTvNkZ5ylY0PGgA+Lih+GdboMLY/G9SEGLMEGVjTVA4= +github.com/petermattis/goid v0.0.0-20250904145737-900bdf8bb490/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7/go.mod h1:zO8QMzTeZd5cpnIkz/Gn6iK0jDfGicM1nynOkkPIl28= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -335,6 +455,8 @@ github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2 github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0= github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw= +github.com/protocolbuffers/protoscope v0.0.0-20221109213918-8e7a6aafa2c9 h1:arwj11zP0yJIxIRiDn22E0H8PxfF7TsTrc2wIPFIsf4= +github.com/protocolbuffers/protoscope v0.0.0-20221109213918-8e7a6aafa2c9/go.mod h1:SKZx6stCn03JN3BOWTwvVIO2ajMkb/zQdTceXYhKw/4= github.com/quasilyte/go-ruleguard v0.4.4 h1:53DncefIeLX3qEpjzlS1lyUmQoUEeOWPFWqaTJq9eAQ= github.com/quasilyte/go-ruleguard v0.4.4/go.mod h1:Vl05zJ538vcEEwu16V/Hdu7IYZWyKSwIy4c88Ro1kRE= github.com/quasilyte/go-ruleguard/dsl v0.3.22 h1:wd8zkOhSNr+I+8Qeciml08ivDt1pSXe60+5DqOpCjPE= @@ -345,6 +467,10 @@ github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 h1:TCg2WBOl github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 h1:M8mH9eK4OUR4lu7Gd+PU1fV2/qnDNfzT635KRSObncs= github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ= +github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= +github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= +github.com/quic-go/quic-go v0.55.0 h1:zccPQIqYCXDt5NmcEabyYvOnomjs8Tlwl7tISjJh9Mk= +github.com/quic-go/quic-go v0.55.0/go.mod h1:DR51ilwU1uE164KuWXhinFcKWGlEjzys2l8zUl5Ss1U= github.com/raeperd/recvcheck v0.2.0 h1:GnU+NsbiCqdC2XX5+vMZzP+jAJC5fht7rcVTAhX74UI= github.com/raeperd/recvcheck v0.2.0/go.mod h1:n04eYkwIR0JbgD73wT8wL4JjPC3wm0nFtzBnWNocnYU= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -352,6 +478,9 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= +github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryancurrah/gomodguard v1.4.1 h1:eWC8eUMNZ/wM/PWuZBv7JxxqT5fiIKSIyTvjb7Elr+g= github.com/ryancurrah/gomodguard v1.4.1/go.mod h1:qnMJwV1hX9m+YJseXEBhd2s90+1Xn6x9dLz11ualI1I= @@ -369,6 +498,10 @@ github.com/sashamelentyev/usestdlibvars v1.29.0 h1:8J0MoRrw4/NAXtjQqTHrbW9NN+3iM github.com/sashamelentyev/usestdlibvars v1.29.0/go.mod h1:8PpnjHMk5VdeWlVb4wCdrB8PNbLqZ3wBZTZWkrpZZL8= github.com/securego/gosec/v2 v2.22.7 h1:8/9P+oTYI4yIpAzccQKVsg1/90Po+JzGtAhqoHImDeM= github.com/securego/gosec/v2 v2.22.7/go.mod h1:510TFNDMrIPytokyHQAVLvPeDr41Yihn2ak8P+XQfNE= +github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0= +github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= +github.com/segmentio/encoding v0.5.3 h1:OjMgICtcSFuNvQCdwqMCv9Tg7lEOXGwm1J5RPQccx6w= +github.com/segmentio/encoding v0.5.3/go.mod h1:HS1ZKa3kSN32ZHVZ7ZLPLXWvOVIiZtyJnO1gPH1sKt0= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= @@ -388,27 +521,34 @@ github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA= github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE= github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= -github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= -github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= +github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= -github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/ssgreg/nlreturn/v2 v2.2.1 h1:X4XDI7jstt3ySqGU86YGAURbxw3oTDPK9sPEi6YEwQ0= github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= github.com/stbenjam/no-sprintf-host-port v0.2.0 h1:i8pxvGrt1+4G0czLr/WnmyH7zbZ8Bg8etvARQ1rpyl4= github.com/stbenjam/no-sprintf-host-port v0.2.0/go.mod h1:eL0bQ9PasS0hsyTyfTjjG+E80QIyPnBVQbYZyv20Jfk= +github.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs= +github.com/stoewer/go-strcase v1.3.1/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 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.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8= @@ -420,6 +560,10 @@ github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpR github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY= github.com/tetafro/godot v1.5.1 h1:PZnjCol4+FqaEzvZg5+O8IY2P3hfY9JzRBNPv1pEDS4= github.com/tetafro/godot v1.5.1/go.mod h1:cCdPtEndkmqqrhiCfkmxDodMQJ/f3L1BCNskCUZdTwk= +github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I= +github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM= +github.com/tidwall/btree v1.8.1 h1:27ehoXvm5AG/g+1VxLS1SD3vRhp/H7LuEfwNvddEdmA= +github.com/tidwall/btree v1.8.1/go.mod h1:jBbTdUWhSZClZWoDg54VnvV7/54modSOzDN7VXftj1A= github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67 h1:9LPGD+jzxMlnk5r6+hJnar67cgpDIz/iyD+rfl5r2Vk= github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67/go.mod h1:mkjARE7Yr8qU23YcGMSALbIxTQ9r9QBVahQOBRfU460= github.com/timonwong/loggercheck v0.11.0 h1:jdaMpYBl+Uq9mWPXv1r8jc5fC3gyXx4/WGwTnnNKn4M= @@ -436,6 +580,8 @@ github.com/uudashr/gocognit v1.2.0 h1:3BU9aMr1xbhPlvJLSydKwdLN3tEUUrzPSSM8S4hDYR github.com/uudashr/gocognit v1.2.0/go.mod h1:k/DdKPI6XBZO1q7HgoV2juESI2/Ofj9AcHPZhBBdrTU= github.com/uudashr/iface v1.4.1 h1:J16Xl1wyNX9ofhpHmQ9h9gk5rnv2A6lX/2+APLTo0zU= github.com/uudashr/iface v1.4.1/go.mod h1:pbeBPlbuU2qkNDn0mmfrxP2X+wjPMIQAy+r1MBXSXtg= +github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo= +github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA= github.com/xen0n/gosmopolitan v1.3.0 h1:zAZI1zefvo7gcpbCOrPSHJZJYA9ZgLfJqtKzZ5pHqQM= github.com/xen0n/gosmopolitan v1.3.0/go.mod h1:rckfr5T6o4lBtM1ga7mLGKZmLxswUoH1zxHgNXOsEt4= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= @@ -466,12 +612,42 @@ go.augendre.info/arangolint v0.2.0 h1:2NP/XudpPmfBhQKX4rMk+zDYIj//qbt4hfZmSSTcpj go.augendre.info/arangolint v0.2.0/go.mod h1:Vx4KSJwu48tkE+8uxuf0cbBnAPgnt8O1KWiT7bljq7w= go.augendre.info/fatcontext v0.8.0 h1:2dfk6CQbDGeu1YocF59Za5Pia7ULeAM6friJ3LP7lmk= go.augendre.info/fatcontext v0.8.0/go.mod h1:oVJfMgwngMsHO+KB2MdgzcO+RvtNdiCEOlWvSFtax/s= +go.lsp.dev/jsonrpc2 v0.10.0 h1:Pr/YcXJoEOTMc/b6OTmcR1DPJ3mSWl/SWiU1Cct6VmI= +go.lsp.dev/jsonrpc2 v0.10.0/go.mod h1:fmEzIdXPi/rf6d4uFcayi8HpFP1nBF99ERP1htC72Ac= +go.lsp.dev/pkg v0.0.0-20210717090340-384b27a52fb2 h1:hCzQgh6UcwbKgNSRurYWSqh8MufqRRPODRBblutn4TE= +go.lsp.dev/pkg v0.0.0-20210717090340-384b27a52fb2/go.mod h1:gtSHRuYfbCT0qnbLnovpie/WEmqyJ7T4n6VXiFMBtcw= +go.lsp.dev/protocol v0.12.0 h1:tNprUI9klQW5FAFVM4Sa+AbPFuVQByWhP1ttNUAjIWg= +go.lsp.dev/protocol v0.12.0/go.mod h1:Qb11/HgZQ72qQbeyPfJbu3hZBH23s1sr4st8czGeDMQ= +go.lsp.dev/uri v0.3.0 h1:KcZJmh6nFIBeJzTugn5JTU6OOyG0lDOo3R9KwTxTYbo= +go.lsp.dev/uri v0.3.0/go.mod h1:P5sbO1IQR+qySTWOCnhnK7phBx+W3zbLqSMDJNTw88I= +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/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= +go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0= +go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= +go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= +go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= +go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= +go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= +go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= +go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= +go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/proto/otlp v1.8.0 h1:fRAZQDcAFHySxpJ1TwlA1cJ4tvcrw7nXl9xWWC8N5CE= +go.opentelemetry.io/proto/otlp v1.8.0/go.mod h1:tIeYOeNBU4cvmPqpaji1P+KbB4Oloai8wN4rWzRrFF0= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= 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/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= +go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= 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= @@ -483,8 +659,10 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 h1:R9PFI6EUdfVKgwKjZef7QIwGcBKu86OEFpJ9nUEP2l4= -golang.org/x/exp v0.0.0-20250718183923-645b1fa84792/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc= +golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= +golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= +golang.org/x/exp v0.0.0-20251009144603-d2f985daa21b h1:18qgiDvlvH7kk8Ioa8Ov+K6xCi0GMvmGfGW0sgd/SYA= +golang.org/x/exp v0.0.0-20251009144603-d2f985daa21b/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70= golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20250718183923-645b1fa84792 h1:54/e+WfmhvjR2Zuz8Q7dzLGxIBM+s5WZpvo1QfVDGB8= @@ -500,8 +678,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg= -golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= +golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= +golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= 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= @@ -516,8 +694,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= -golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= +golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= +golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -527,8 +705,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -540,24 +718,30 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/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-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= -golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= +golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8 h1:LvzTn0GQhWuvKH/kVRS3R3bVAsdQWI7hvfLHGgh9+lU= +golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8/go.mod h1:Pi4ztBfryZoJEkyFTI5/Ocsu2jXyDr6iSdgJiYE/uwE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= +golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= 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= @@ -566,8 +750,10 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= -golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= +golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -589,24 +775,30 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= -golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= -golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= +golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= +golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= +golang.org/x/tools/cmd/godoc v0.1.0-deprecated h1:sEGTwp9aZNTHsdf/2BGaRqE4ZLndRVH17rbQ2OVun9Q= +golang.org/x/tools/cmd/godoc v0.1.0-deprecated/go.mod h1:J6VY4iFch6TIm456U3fnw1EJZaIqcYlhHu6GpHQ9HJk= golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM= golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8= +golang.org/x/tools/godoc v0.1.0-deprecated h1:o+aZ1BOj6Hsx/GBdJO/s815sqftjSnrZZwyYTHODvtk= +golang.org/x/tools/godoc v0.1.0-deprecated/go.mod h1:qM63CriJ961IHWmnWa9CjZnBndniPt4a3CK0PVB9bIg= 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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= -google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= -google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= +google.golang.org/genproto/googleapis/api v0.0.0-20251007200510-49b9836ed3ff h1:8Zg5TdmcbU8A7CXGjGXF1Slqu/nIFCRaR3S5gT2plIA= +google.golang.org/genproto/googleapis/api v0.0.0-20251007200510-49b9836ed3ff/go.mod h1:dbWfpVPvW/RqafStmRWBUpMN14puDezDMHxNYiRfQu0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251007200510-49b9836ed3ff h1:A90eA31Wq6HOMIQlLfzFwzqGKBTuaVztYu/g8sn+8Zc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251007200510-49b9836ed3ff/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= +google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI= +google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 h1:F29+wU6Ee6qgu9TddPgooOdaqsxTMunOoj8KA5yuS5A= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1/go.mod h1:5KF+wpkbTSbGcR9zteSqZV6fqFOWBl4Yde8En8MryZA= -google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= -google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/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= @@ -618,6 +810,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C 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= +gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= +gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.6.1 h1:R094WgE8K4JirYjBaOpz/AvTyUu/3wbmAoskKN/pxTI= honnef.co/go/tools v0.6.1/go.mod h1:3puzxxljPCe8RGJX7BIy1plGbxEOZni5mR2aXe3/uk4= moul.io/banner v1.0.1 h1:+WsemGLhj2pOajw2eR5VYjLhOIqs0XhIRYchzTyMLk0= @@ -634,3 +828,5 @@ mvdan.cc/gofumpt v0.8.0 h1:nZUCeC2ViFaerTcYKstMmfysj6uhQrA2vJe+2vwGU6k= mvdan.cc/gofumpt v0.8.0/go.mod h1:vEYnSzyGPmjvFkqJWtXkh79UwPWP9/HMxQdGEXZHjpg= mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4 h1:WjUu4yQoT5BHT1w8Zu56SP8367OuBV5jvo+4Ulppyf8= mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4/go.mod h1:rthT7OuvRbaGcd5ginj6dA2oLE7YNlta9qhBNNdCaLE= +pluginrpc.com/pluginrpc v0.5.0 h1:tOQj2D35hOmvHyPu8e7ohW2/QvAnEtKscy2IJYWQ2yo= +pluginrpc.com/pluginrpc v0.5.0/go.mod h1:UNWZ941hcVAoOZUn8YZsMmOZBzbUjQa3XMns8RQLp9o= diff --git a/misc/loop/go.mod b/misc/loop/go.mod index 380edbef266..f7dba203e55 100644 --- a/misc/loop/go.mod +++ b/misc/loop/go.mod @@ -14,6 +14,7 @@ replace github.com/gnolang/gno => ../.. replace github.com/gnolang/gno/contribs/tx-archive => ../../contribs/tx-archive require ( + connectrpc.com/connect v1.18.1 // indirect dario.cat/mergo v1.0.2 // indirect github.com/DataDog/zstd v1.4.5 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect diff --git a/misc/loop/go.sum b/misc/loop/go.sum index c16d1a479a7..c0f1a73fbea 100644 --- a/misc/loop/go.sum +++ b/misc/loop/go.sum @@ -1,3 +1,5 @@ +connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw= +connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= diff --git a/misc/stress-test/stress-test-many-posts/go.mod b/misc/stress-test/stress-test-many-posts/go.mod index dd26f99c422..751d6396e74 100644 --- a/misc/stress-test/stress-test-many-posts/go.mod +++ b/misc/stress-test/stress-test-many-posts/go.mod @@ -5,7 +5,7 @@ go 1.23.0 toolchain go1.23.8 require ( - connectrpc.com/connect v1.16.2 + connectrpc.com/connect v1.18.1 github.com/gnolang/gnonative/v4 v4.2.2 ) diff --git a/misc/stress-test/stress-test-many-posts/go.sum b/misc/stress-test/stress-test-many-posts/go.sum index 319505bff4a..f8e07665f75 100644 --- a/misc/stress-test/stress-test-many-posts/go.sum +++ b/misc/stress-test/stress-test-many-posts/go.sum @@ -1,5 +1,5 @@ -connectrpc.com/connect v1.16.2 h1:ybd6y+ls7GOlb7Bh5C8+ghA6SvCBajHwxssO2CGFjqE= -connectrpc.com/connect v1.16.2/go.mod h1:n2kgwskMHXC+lVqb18wngEpF95ldBHXjZYJussz5FRc= +connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw= +connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= connectrpc.com/grpchealth v1.3.0 h1:FA3OIwAvuMokQIXQrY5LbIy8IenftksTP/lG4PbYN+E= connectrpc.com/grpchealth v1.3.0/go.mod h1:3vpqmX25/ir0gVgW6RdnCPPZRcR6HvqtXX5RNPmDXHM= connectrpc.com/grpcreflect v1.2.0 h1:Q6og1S7HinmtbEuBvARLNwYmTbhEGRpHDhqrPNlmK+U= diff --git a/tm2/Makefile b/tm2/Makefile index dbafbeed4c6..9d79cbd29fe 100644 --- a/tm2/Makefile +++ b/tm2/Makefile @@ -60,4 +60,4 @@ _test.pkg.db:; go test $(GOTEST_FLAGS) ./pkg/db/... ./pkg/iavl/benchmarks/. .PHONY: generate generate: go generate -x ./... - $(MAKE) fmt + $(MAKE) fmt \ No newline at end of file diff --git a/tm2/pkg/bft/backup/.gitignore b/tm2/pkg/bft/backup/.gitignore new file mode 100644 index 00000000000..fefa436400e --- /dev/null +++ b/tm2/pkg/bft/backup/.gitignore @@ -0,0 +1 @@ +!*.pb.go \ No newline at end of file diff --git a/tm2/pkg/bft/backup/backup_svc.go b/tm2/pkg/bft/backup/backup_svc.go new file mode 100644 index 00000000000..3fe0b416c7e --- /dev/null +++ b/tm2/pkg/bft/backup/backup_svc.go @@ -0,0 +1,100 @@ +package backup + +import ( + "context" + "fmt" + "net/http" + "time" + + "connectrpc.com/connect" + "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/bft/backup/backuppb" + "github.com/gnolang/gno/tm2/pkg/bft/backup/backuppb/backuppbconnect" + "github.com/gnolang/gno/tm2/pkg/bft/types" + "golang.org/x/net/http2" + "golang.org/x/net/http2/h2c" +) + +type Config struct { + // Address for the backup server to listen on. Empty means disabled. + ListenAddress string `json:"laddr" toml:"laddr" comment:"Address for the backup server to listen on. Empty means disabled."` +} + +func DefaultConfig() *Config { + return &Config{} +} + +type blockStore interface { + Height() int64 + LoadBlock(height int64) *types.Block +} + +func NewBackupServiceHandler(store blockStore) (string, http.Handler) { + backupServ := &backupServer{store: store} + return backuppbconnect.NewBackupServiceHandler(backupServ) +} + +func NewMux(store blockStore) *http.ServeMux { + mux := http.NewServeMux() + mux.Handle(NewBackupServiceHandler(store)) + return mux +} + +func NewServer(conf *Config, store blockStore) *http.Server { + mux := NewMux(store) + return &http.Server{Addr: conf.ListenAddress, Handler: h2c.NewHandler(mux, &http2.Server{}), ReadHeaderTimeout: time.Second * 5} +} + +type backupServer struct { + store blockStore +} + +// StreamBlocks implements backuppbconnect.BackupServiceHandler. +func (b *backupServer) StreamBlocks(_ context.Context, req *connect.Request[backuppb.StreamBlocksRequest], stream *connect.ServerStream[backuppb.StreamBlocksResponse]) error { + if req == nil || req.Msg == nil { + return fmt.Errorf("request is nil") + } + startHeight := req.Msg.StartHeight + if startHeight == 0 { + startHeight = 1 + } + if startHeight < 1 { + return fmt.Errorf("start height must be >= 1, got %d", startHeight) + } + + blockStoreHeight := b.store.Height() + if blockStoreHeight < 1 { + return fmt.Errorf("block store returned invalid max height (%d)", blockStoreHeight) + } + + endHeight := req.Msg.EndHeight + if endHeight == 0 { + endHeight = blockStoreHeight + } else if endHeight > blockStoreHeight { + return fmt.Errorf("end height must be <= %d", blockStoreHeight) + } + + if startHeight > endHeight { + return fmt.Errorf("end height must be >= than start height") + } + + for height := startHeight; height <= endHeight; height++ { + block := b.store.LoadBlock(height) + if block == nil { + return fmt.Errorf("block store returned nil block for height %d", height) + } + + data, err := amino.Marshal(block) + if err != nil { + return err + } + + if err := stream.Send(&backuppb.StreamBlocksResponse{Data: data}); err != nil { + return err + } + } + + return nil +} + +var _ backuppbconnect.BackupServiceHandler = (*backupServer)(nil) diff --git a/tm2/pkg/bft/backup/backup_svc_test.go b/tm2/pkg/bft/backup/backup_svc_test.go new file mode 100644 index 00000000000..c7d8a86f9ac --- /dev/null +++ b/tm2/pkg/bft/backup/backup_svc_test.go @@ -0,0 +1,219 @@ +package backup + +import ( + "context" + "net/http/httptest" + "testing" + + "connectrpc.com/connect" + "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/bft/backup/backuppb" + "github.com/gnolang/gno/tm2/pkg/bft/backup/backuppb/backuppbconnect" + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/stretchr/testify/require" +) + +func TestStreamBlocks(t *testing.T) { + tcs := []struct { + name string + initStore func() *mockBlockStore + start int64 + end int64 + expectedResult []*types.Block + errContains string + }{ + { + name: "one block store", + initStore: func() *mockBlockStore { + return &mockBlockStore{height: 1, blocks: map[int64]*types.Block{ + 1: {Header: types.Header{Height: 1}}, + }} + }, + expectedResult: []*types.Block{ + {Header: types.Header{Height: 1}}, + }, + }, + { + name: "range", + start: 2, + end: 4, + initStore: func() *mockBlockStore { + return &mockBlockStore{height: 5, blocks: map[int64]*types.Block{ + 1: {Header: types.Header{Height: 1}}, + 2: {Header: types.Header{Height: 2}}, + 3: {Header: types.Header{Height: 3}}, + 4: {Header: types.Header{Height: 4}}, + 5: {Header: types.Header{Height: 5}}, + }} + }, + expectedResult: []*types.Block{ + {Header: types.Header{Height: 2}}, + {Header: types.Header{Height: 3}}, + {Header: types.Header{Height: 4}}, + }, + }, + { + name: "range no start", + end: 4, + initStore: func() *mockBlockStore { + return &mockBlockStore{height: 5, blocks: map[int64]*types.Block{ + 1: {Header: types.Header{Height: 1}}, + 2: {Header: types.Header{Height: 2}}, + 3: {Header: types.Header{Height: 3}}, + 4: {Header: types.Header{Height: 4}}, + 5: {Header: types.Header{Height: 5}}, + }} + }, + expectedResult: []*types.Block{ + {Header: types.Header{Height: 1}}, + {Header: types.Header{Height: 2}}, + {Header: types.Header{Height: 3}}, + {Header: types.Header{Height: 4}}, + }, + }, + { + name: "range no end", + start: 2, + initStore: func() *mockBlockStore { + return &mockBlockStore{height: 5, blocks: map[int64]*types.Block{ + 1: {Header: types.Header{Height: 1}}, + 2: {Header: types.Header{Height: 2}}, + 3: {Header: types.Header{Height: 3}}, + 4: {Header: types.Header{Height: 4}}, + 5: {Header: types.Header{Height: 5}}, + }} + }, + expectedResult: []*types.Block{ + {Header: types.Header{Height: 2}}, + {Header: types.Header{Height: 3}}, + {Header: types.Header{Height: 4}}, + {Header: types.Header{Height: 5}}, + }, + }, + { + name: "range no params", + initStore: func() *mockBlockStore { + return &mockBlockStore{height: 5, blocks: map[int64]*types.Block{ + 1: {Header: types.Header{Height: 1}}, + 2: {Header: types.Header{Height: 2}}, + 3: {Header: types.Header{Height: 3}}, + 4: {Header: types.Header{Height: 4}}, + 5: {Header: types.Header{Height: 5}}, + }} + }, + expectedResult: []*types.Block{ + {Header: types.Header{Height: 1}}, + {Header: types.Header{Height: 2}}, + {Header: types.Header{Height: 3}}, + {Header: types.Header{Height: 4}}, + {Header: types.Header{Height: 5}}, + }, + }, + { + name: "err nil block", + initStore: func() *mockBlockStore { + return &mockBlockStore{height: 3, blocks: map[int64]*types.Block{ + 1: {Header: types.Header{Height: 1}}, + 2: nil, + 3: {Header: types.Header{Height: 3}}, + }} + }, + errContains: "block store returned nil block for height 2", + expectedResult: []*types.Block{ + {Header: types.Header{Height: 1}}, + }, + }, + { + name: "err empty store", + initStore: func() *mockBlockStore { + return &mockBlockStore{} + }, + errContains: "block store returned invalid max height (0)", + }, + { + name: "err reverse range", + start: 2, + end: 1, + initStore: func() *mockBlockStore { + return &mockBlockStore{height: 1, blocks: map[int64]*types.Block{1: {}, 2: {}}} + }, + errContains: "end height must be >= than start height", + }, + { + name: "err invalid start", + start: -42, + initStore: func() *mockBlockStore { + return &mockBlockStore{height: 1, blocks: map[int64]*types.Block{1: {Header: types.Header{Height: 1}}}} + }, + errContains: "start height must be >= 1, got -42", + }, + { + name: "err invalid end", + end: 42, + initStore: func() *mockBlockStore { + return &mockBlockStore{height: 1, blocks: map[int64]*types.Block{1: {}}} + }, + errContains: "end height must be <= 1", + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + store := tc.initStore() + mux := NewMux(store) + srv := httptest.NewServer(mux) + defer srv.Close() + httpClient := srv.Client() + client := backuppbconnect.NewBackupServiceClient(httpClient, srv.URL) + + stream, err := client.StreamBlocks(context.Background(), &connect.Request[backuppb.StreamBlocksRequest]{Msg: &backuppb.StreamBlocksRequest{ + StartHeight: tc.start, + EndHeight: tc.end, + }}) + require.NoError(t, err) + defer func() { + require.NoError(t, stream.Close()) + }() + + data := []*types.Block(nil) + for { + if !stream.Receive() { + err := stream.Err() + if tc.errContains == "" { + require.NoError(t, err) + } else { + require.ErrorContains(t, err, tc.errContains) + } + break + } + msg := stream.Msg() + block := &types.Block{} + require.NoError(t, amino.Unmarshal(msg.Data, block)) + data = append(data, block) + } + require.Equal(t, tc.expectedResult, data) + }) + } +} + +func TestNewServer(t *testing.T) { + require.NotPanics(t, func() { + serv := NewServer(DefaultConfig(), &mockBlockStore{}) + require.NotNil(t, serv) + }) +} + +type mockBlockStore struct { + height int64 + blocks map[int64]*types.Block +} + +// Height implements blockStore. +func (m *mockBlockStore) Height() int64 { + return m.height +} + +// LoadBlock implements blockStore. +func (m *mockBlockStore) LoadBlock(height int64) *types.Block { + return m.blocks[height] +} diff --git a/tm2/pkg/bft/backup/backuppb/backup.pb.go b/tm2/pkg/bft/backup/backuppb/backup.pb.go new file mode 100644 index 00000000000..181a5a22a4b --- /dev/null +++ b/tm2/pkg/bft/backup/backuppb/backup.pb.go @@ -0,0 +1,184 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.10 +// protoc (unknown) +// source: backuppb/backup.proto + +package backuppb + +import ( + reflect "reflect" + sync "sync" + unsafe "unsafe" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type StreamBlocksRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + StartHeight int64 `protobuf:"varint,1,opt,name=start_height,json=startHeight,proto3" json:"start_height,omitempty"` + EndHeight int64 `protobuf:"varint,2,opt,name=end_height,json=endHeight,proto3" json:"end_height,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StreamBlocksRequest) Reset() { + *x = StreamBlocksRequest{} + mi := &file_backuppb_backup_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StreamBlocksRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StreamBlocksRequest) ProtoMessage() {} + +func (x *StreamBlocksRequest) ProtoReflect() protoreflect.Message { + mi := &file_backuppb_backup_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StreamBlocksRequest.ProtoReflect.Descriptor instead. +func (*StreamBlocksRequest) Descriptor() ([]byte, []int) { + return file_backuppb_backup_proto_rawDescGZIP(), []int{0} +} + +func (x *StreamBlocksRequest) GetStartHeight() int64 { + if x != nil { + return x.StartHeight + } + return 0 +} + +func (x *StreamBlocksRequest) GetEndHeight() int64 { + if x != nil { + return x.EndHeight + } + return 0 +} + +type StreamBlocksResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StreamBlocksResponse) Reset() { + *x = StreamBlocksResponse{} + mi := &file_backuppb_backup_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StreamBlocksResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StreamBlocksResponse) ProtoMessage() {} + +func (x *StreamBlocksResponse) ProtoReflect() protoreflect.Message { + mi := &file_backuppb_backup_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StreamBlocksResponse.ProtoReflect.Descriptor instead. +func (*StreamBlocksResponse) Descriptor() ([]byte, []int) { + return file_backuppb_backup_proto_rawDescGZIP(), []int{1} +} + +func (x *StreamBlocksResponse) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +var File_backuppb_backup_proto protoreflect.FileDescriptor + +const file_backuppb_backup_proto_rawDesc = "" + + "\n" + + "\x15backuppb/backup.proto\x12\x02tm\"W\n" + + "\x13StreamBlocksRequest\x12!\n" + + "\fstart_height\x18\x01 \x01(\x03R\vstartHeight\x12\x1d\n" + + "\n" + + "end_height\x18\x02 \x01(\x03R\tendHeight\"*\n" + + "\x14StreamBlocksResponse\x12\x12\n" + + "\x04data\x18\x02 \x01(\fR\x04data2T\n" + + "\rBackupService\x12C\n" + + "\fStreamBlocks\x12\x17.tm.StreamBlocksRequest\x1a\x18.tm.StreamBlocksResponse0\x01B=Z;github.com/gnolang/gno/tm2/pkg/bft/backup/backuppb;backuppbb\x06proto3" + +var ( + file_backuppb_backup_proto_rawDescOnce sync.Once + file_backuppb_backup_proto_rawDescData []byte +) + +func file_backuppb_backup_proto_rawDescGZIP() []byte { + file_backuppb_backup_proto_rawDescOnce.Do(func() { + file_backuppb_backup_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_backuppb_backup_proto_rawDesc), len(file_backuppb_backup_proto_rawDesc))) + }) + return file_backuppb_backup_proto_rawDescData +} + +var file_backuppb_backup_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_backuppb_backup_proto_goTypes = []any{ + (*StreamBlocksRequest)(nil), // 0: tm.StreamBlocksRequest + (*StreamBlocksResponse)(nil), // 1: tm.StreamBlocksResponse +} +var file_backuppb_backup_proto_depIdxs = []int32{ + 0, // 0: tm.BackupService.StreamBlocks:input_type -> tm.StreamBlocksRequest + 1, // 1: tm.BackupService.StreamBlocks:output_type -> tm.StreamBlocksResponse + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_backuppb_backup_proto_init() } +func file_backuppb_backup_proto_init() { + if File_backuppb_backup_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_backuppb_backup_proto_rawDesc), len(file_backuppb_backup_proto_rawDesc)), + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_backuppb_backup_proto_goTypes, + DependencyIndexes: file_backuppb_backup_proto_depIdxs, + MessageInfos: file_backuppb_backup_proto_msgTypes, + }.Build() + File_backuppb_backup_proto = out.File + file_backuppb_backup_proto_goTypes = nil + file_backuppb_backup_proto_depIdxs = nil +} diff --git a/tm2/pkg/bft/backup/backuppb/backup.proto b/tm2/pkg/bft/backup/backuppb/backup.proto new file mode 100644 index 00000000000..7b257c08bfc --- /dev/null +++ b/tm2/pkg/bft/backup/backuppb/backup.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; +package tm; + +option go_package = "github.com/gnolang/gno/tm2/pkg/bft/backup/backuppb;backuppb"; + +service BackupService { + rpc StreamBlocks (StreamBlocksRequest) returns (stream StreamBlocksResponse); +} + +message StreamBlocksRequest { + int64 start_height = 1; + int64 end_height = 2; +} + +message StreamBlocksResponse { + bytes data = 2; +} \ No newline at end of file diff --git a/tm2/pkg/bft/backup/backuppb/backuppbconnect/backup.connect.go b/tm2/pkg/bft/backup/backuppb/backuppbconnect/backup.connect.go new file mode 100644 index 00000000000..81ec75cdaa0 --- /dev/null +++ b/tm2/pkg/bft/backup/backuppb/backuppbconnect/backup.connect.go @@ -0,0 +1,110 @@ +// Code generated by protoc-gen-connect-go. DO NOT EDIT. +// +// Source: backuppb/backup.proto + +package backuppbconnect + +import ( + context "context" + errors "errors" + http "net/http" + strings "strings" + + connect "connectrpc.com/connect" + backuppb "github.com/gnolang/gno/tm2/pkg/bft/backup/backuppb" +) + +// This is a compile-time assertion to ensure that this generated file and the connect package are +// compatible. If you get a compiler error that this constant is not defined, this code was +// generated with a version of connect newer than the one compiled into your binary. You can fix the +// problem by either regenerating this code with an older version of connect or updating the connect +// version compiled into your binary. +const _ = connect.IsAtLeastVersion1_13_0 + +const ( + // BackupServiceName is the fully-qualified name of the BackupService service. + BackupServiceName = "tm.BackupService" +) + +// These constants are the fully-qualified names of the RPCs defined in this package. They're +// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route. +// +// Note that these are different from the fully-qualified method names used by +// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to +// reflection-formatted method names, remove the leading slash and convert the remaining slash to a +// period. +const ( + // BackupServiceStreamBlocksProcedure is the fully-qualified name of the BackupService's + // StreamBlocks RPC. + BackupServiceStreamBlocksProcedure = "/tm.BackupService/StreamBlocks" +) + +// BackupServiceClient is a client for the tm.BackupService service. +type BackupServiceClient interface { + StreamBlocks(context.Context, *connect.Request[backuppb.StreamBlocksRequest]) (*connect.ServerStreamForClient[backuppb.StreamBlocksResponse], error) +} + +// NewBackupServiceClient constructs a client for the tm.BackupService service. By default, it uses +// the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends +// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or +// connect.WithGRPCWeb() options. +// +// The URL supplied here should be the base URL for the Connect or gRPC server (for example, +// http://api.acme.com or https://acme.com/grpc). +func NewBackupServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) BackupServiceClient { + baseURL = strings.TrimRight(baseURL, "/") + backupServiceMethods := backuppb.File_backuppb_backup_proto.Services().ByName("BackupService").Methods() + return &backupServiceClient{ + streamBlocks: connect.NewClient[backuppb.StreamBlocksRequest, backuppb.StreamBlocksResponse]( + httpClient, + baseURL+BackupServiceStreamBlocksProcedure, + connect.WithSchema(backupServiceMethods.ByName("StreamBlocks")), + connect.WithClientOptions(opts...), + ), + } +} + +// backupServiceClient implements BackupServiceClient. +type backupServiceClient struct { + streamBlocks *connect.Client[backuppb.StreamBlocksRequest, backuppb.StreamBlocksResponse] +} + +// StreamBlocks calls tm.BackupService.StreamBlocks. +func (c *backupServiceClient) StreamBlocks(ctx context.Context, req *connect.Request[backuppb.StreamBlocksRequest]) (*connect.ServerStreamForClient[backuppb.StreamBlocksResponse], error) { + return c.streamBlocks.CallServerStream(ctx, req) +} + +// BackupServiceHandler is an implementation of the tm.BackupService service. +type BackupServiceHandler interface { + StreamBlocks(context.Context, *connect.Request[backuppb.StreamBlocksRequest], *connect.ServerStream[backuppb.StreamBlocksResponse]) error +} + +// NewBackupServiceHandler builds an HTTP handler from the service implementation. It returns the +// path on which to mount the handler and the handler itself. +// +// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf +// and JSON codecs. They also support gzip compression. +func NewBackupServiceHandler(svc BackupServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) { + backupServiceMethods := backuppb.File_backuppb_backup_proto.Services().ByName("BackupService").Methods() + backupServiceStreamBlocksHandler := connect.NewServerStreamHandler( + BackupServiceStreamBlocksProcedure, + svc.StreamBlocks, + connect.WithSchema(backupServiceMethods.ByName("StreamBlocks")), + connect.WithHandlerOptions(opts...), + ) + return "/tm.BackupService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case BackupServiceStreamBlocksProcedure: + backupServiceStreamBlocksHandler.ServeHTTP(w, r) + default: + http.NotFound(w, r) + } + }) +} + +// UnimplementedBackupServiceHandler returns CodeUnimplemented from all methods. +type UnimplementedBackupServiceHandler struct{} + +func (UnimplementedBackupServiceHandler) StreamBlocks(context.Context, *connect.Request[backuppb.StreamBlocksRequest], *connect.ServerStream[backuppb.StreamBlocksResponse]) error { + return connect.NewError(connect.CodeUnimplemented, errors.New("tm.BackupService.StreamBlocks is not implemented")) +} diff --git a/tm2/pkg/bft/backup/buf.gen.yaml b/tm2/pkg/bft/backup/buf.gen.yaml new file mode 100644 index 00000000000..64c1cf12c92 --- /dev/null +++ b/tm2/pkg/bft/backup/buf.gen.yaml @@ -0,0 +1,9 @@ +version: v2 +plugins: + - local: [go, run, -modfile, ../../../../misc/devdeps/go.mod, google.golang.org/protobuf/cmd/protoc-gen-go] + out: . + opt: paths=source_relative + + - local: [go, run, -modfile, ../../../../misc/devdeps/go.mod, connectrpc.com/connect/cmd/protoc-gen-connect-go] + out: . + opt: paths=source_relative \ No newline at end of file diff --git a/tm2/pkg/bft/backup/buf.yaml b/tm2/pkg/bft/backup/buf.yaml new file mode 100644 index 00000000000..5a15800e540 --- /dev/null +++ b/tm2/pkg/bft/backup/buf.yaml @@ -0,0 +1,10 @@ +# For details on buf.yaml configuration, visit https://buf.build/docs/configuration/v2/buf-yaml +version: v2 +modules: + - path: . +lint: + use: + - STANDARD +breaking: + use: + - FILE diff --git a/tm2/pkg/bft/backup/gen.go b/tm2/pkg/bft/backup/gen.go new file mode 100644 index 00000000000..1213c9ab049 --- /dev/null +++ b/tm2/pkg/bft/backup/gen.go @@ -0,0 +1,3 @@ +package backup + +//go:generate go run -modfile ../../../../misc/devdeps/go.mod github.com/bufbuild/buf/cmd/buf generate diff --git a/tm2/pkg/bft/backup/v1/backup_test.go b/tm2/pkg/bft/backup/v1/backup_test.go new file mode 100644 index 00000000000..7914c064a61 --- /dev/null +++ b/tm2/pkg/bft/backup/v1/backup_test.go @@ -0,0 +1,42 @@ +package backup + +import ( + "testing" + + "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/stretchr/testify/require" +) + +func TestInfo(t *testing.T) { + dir := t.TempDir() + + testWriteBlocks(t, dir, 142, 1) + + state, err := readState(dir) + require.NoError(t, err) + require.Equal(t, int64(1), state.StartHeight, "expected valid initial height after first write") + require.Equal(t, int64(142), state.EndHeight, "expected valid end height after first write") + + testWriteBlocks(t, dir, 142, 101) + + state, err = readState(dir) + require.NoError(t, err) + require.Equal(t, int64(1), state.StartHeight, "expected same initial height after second write") + require.Equal(t, int64(242), state.EndHeight, "expected valid end height after second write") +} + +func testWriteBlocks(t *testing.T, dir string, count int64, expectedStart int64) { + t.Helper() + require.NoError(t, WithWriter(dir, 0, 0, nil, func(startHeight int64, write Writer) error { + require.Equal(t, expectedStart, startHeight, "expected correct start height in callback") + for i := range count { + data, err := amino.Marshal(&types.Block{Header: types.Header{Height: i + startHeight}}) + require.NoError(t, err) + if err := write(data); err != nil { + return err + } + } + return nil + })) +} diff --git a/tm2/pkg/bft/backup/v1/reader.go b/tm2/pkg/bft/backup/v1/reader.go new file mode 100644 index 00000000000..f84e320abc2 --- /dev/null +++ b/tm2/pkg/bft/backup/v1/reader.go @@ -0,0 +1,153 @@ +package backup + +import ( + "archive/tar" + "errors" + "fmt" + "io" + "os" + "path/filepath" + "strconv" + + "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/klauspost/compress/zstd" +) + +type Reader = func(yield func(block *types.Block) error) error + +// WithReader creates a backup reader and pass it to the provided cb. +// yield should not be called concurently +func WithReader(dir string, startHeight int64, endHeight int64, cb func(reader Reader) error) (resErr error) { + dir = filepath.Clean(dir) + + if endHeight != 0 && endHeight < startHeight { + return fmt.Errorf("requested end height (%d) is smaller than requested start height (%d)", endHeight, startHeight) + } + + unlock, err := lockDir(dir) + if err != nil { + return fmt.Errorf("lock output directory %q: %w", dir, err) + } + defer func() { + resErr = errors.Join(unlock(), resErr) + }() + + state, err := readState(dir) + if err != nil { + return err + } + + if state.StartHeight < 1 || state.EndHeight < 1 { + return errors.New("invalid backup state") + } + + if startHeight < state.StartHeight { + return fmt.Errorf("requested start height (#%d) is smaller than backup start height (#%d)", startHeight, state.StartHeight) + } + + if endHeight == 0 { + endHeight = state.EndHeight + } else if endHeight > state.EndHeight { + return fmt.Errorf("requested end height (#%d) is greater than backup end height (#%d)", endHeight, state.EndHeight) + } + + chunkHeight := startHeight - (startHeight-1)%ChunkSize + + reader := &backupReader{ + dir: dir, + startHeight: startHeight, + endHeight: endHeight, + chunkHeight: chunkHeight, + height: startHeight, + } + + return cb(reader.read) +} + +type backupReader struct { + dir string + startHeight int64 + endHeight int64 + chunkHeight int64 + height int64 +} + +func (c *backupReader) read(yield func(block *types.Block) error) error { + for { + err := c.readChunk(yield) + switch { + case errors.Is(err, errReadDone): + return nil + case err != nil: + return err + } + } +} + +func (c *backupReader) readChunk(yield func(block *types.Block) error) (retErr error) { + chunkFP := getChunkFP(c.dir, c.chunkHeight) + + chunk, err := os.Open(chunkFP) + if err != nil { + return err + } + defer func() { + retErr = errors.Join(chunk.Close(), retErr) + }() + + zstr, err := zstd.NewReader(chunk) + if err != nil { + return err + } + defer zstr.Close() + + r := tar.NewReader(zstr) + for { + h, err := r.Next() + if err != nil { + if errors.Is(err, io.EOF) { + break + } + return err + } + + blockHeight, err := strconv.ParseInt(h.Name, 10, 64) + if err != nil { + return err + } + + if blockHeight < c.startHeight { + continue + } + + if blockHeight != c.height { + return fmt.Errorf("unexpected block height: wanted %d, got %d", c.height, blockHeight) + } + + blockBz := make([]byte, h.Size) + if _, err := r.Read(blockBz); err != nil && !errors.Is(err, io.EOF) { + return err + } + block := &types.Block{} + if err := amino.Unmarshal(blockBz, block); err != nil { + return err + } + + if err := yield(block); err != nil { + return err + } + + if c.height == c.endHeight { + return errReadDone + } + + c.height += 1 + } + + c.chunkHeight += ChunkSize + + return nil +} + +var errReadDone = errors.New("read done") diff --git a/tm2/pkg/bft/backup/v1/util.go b/tm2/pkg/bft/backup/v1/util.go new file mode 100644 index 00000000000..16edc6a4fc3 --- /dev/null +++ b/tm2/pkg/bft/backup/v1/util.go @@ -0,0 +1,87 @@ +package backup + +import ( + "encoding/json" + "errors" + "fmt" + "os" + "path/filepath" + + "github.com/gofrs/flock" +) + +const ( + ChunkSize = 100 + backupStateFilename = "info.json" + backupVersion = "v1" +) + +func lockDir(dir string) (func() error, error) { + if err := os.MkdirAll(dir, 0o775); err != nil { + return nil, fmt.Errorf("failed to ensure output directory exists: %w", err) + } + + fileLock := flock.New(filepath.Join(dir, "blocks.lock")) + locked, err := fileLock.TryLock() + if err != nil { + return nil, err + } + if !locked { + return nil, errors.New("failed to acquire lock on output directory") + } + return fileLock.Unlock, nil +} + +func getChunkFP(dir string, chunkHeight int64) string { + return filepath.Join(dir, fmt.Sprintf("%019d.tm2blocks"+archiveSuffix, chunkHeight)) +} + +type backupState struct { + Version string + StartHeight int64 + EndHeight int64 + + filepath string +} + +func (s *backupState) save() error { + bz, err := json.Marshal(s) + if err != nil { + return err + } + if err := os.WriteFile(s.filepath, bz, 0o664); err != nil { + return err + } + + return nil +} + +func readState(dir string) (*backupState, error) { + fp := filepath.Join(dir, backupStateFilename) + + stateBz, err := os.ReadFile(fp) + switch { + case os.IsNotExist(err): + return &backupState{ + StartHeight: -1, + EndHeight: -1, + Version: backupVersion, + filepath: fp, + }, nil + case err != nil: + return nil, err + } + + state := backupState{} + if err := json.Unmarshal(stateBz, &state); err != nil { + return nil, err + } + + if state.Version != backupVersion { + return nil, fmt.Errorf("backup version mismatch, expected %q (binary), got %q (directory)", backupVersion, state.Version) + } + + state.filepath = fp + + return &state, nil +} diff --git a/tm2/pkg/bft/backup/v1/writer.go b/tm2/pkg/bft/backup/v1/writer.go new file mode 100644 index 00000000000..9900cd0660e --- /dev/null +++ b/tm2/pkg/bft/backup/v1/writer.go @@ -0,0 +1,234 @@ +package backup + +import ( + "archive/tar" + "errors" + "fmt" + "os" + "path/filepath" + + "github.com/klauspost/compress/zstd" + "go.uber.org/zap" +) + +const ( + archiveSuffix = ".tar.zst" + nextChunkFilename = "next-chunk" + archiveSuffix +) + +type Writer = func(bytes []byte) error + +// WithWriter creates a backup writer and pass it to the provided cb. +// write should not be called concurently +func WithWriter(dir string, startHeightReq int64, endHeight int64, logger *zap.Logger, cb func(startHeight int64, write Writer) error) (retErr error) { + if startHeightReq < 0 { + return errors.New("start height request must be >= 0") + } + if endHeight < 0 { + return errors.New("end height must be >= 0") + } + + if logger == nil { + logger = zap.NewNop() + } + + dir = filepath.Clean(dir) + + unlock, err := lockDir(dir) + if err != nil { + return fmt.Errorf("lock output directory %q: %w", dir, err) + } + defer func() { + retErr = errors.Join(unlock(), retErr) + }() + + state, err := readState(dir) + if err != nil { + return err + } + + if endHeight != 0 && endHeight == state.EndHeight { + logger.Info("Nothing to do, backup is already at requested end height") + return nil + } + + if endHeight != 0 && endHeight <= state.EndHeight { + return fmt.Errorf("invalid input: requested end height is smaller or equal to the existing backup height (#%d), use a different output directory or a valid end height", state.EndHeight) + } + + height, err := getStartHeight(startHeightReq, state.EndHeight) + if err != nil { + return fmt.Errorf("decide start height: %w", err) + } + + if state.StartHeight == -1 { + state.StartHeight = height + } + + prefix := "Starting backup" + if state.EndHeight != -1 { + prefix = "Resuming backup" + } + logger.Info(prefix, zap.Int64("height", height), zap.String("dir", dir), zap.Int64("end", endHeight)) + + writer := writerImpl{ + dir: dir, + chunkStart: height, + nextHeight: height, + logger: logger, + state: state, + } + + if err := writer.openChunk(); err != nil { + return fmt.Errorf("open first chunk: %w", err) + } + defer func() { + retErr = errors.Join(writer.finalizeChunk(), retErr) + }() + + return cb(height, writer.write) +} + +type writerImpl struct { + dir string + nextHeight int64 + chunkStart int64 + outFile *os.File + logger *zap.Logger + zstw *zstd.Encoder + w *tar.Writer + poisoned bool + state *backupState +} + +func (b *writerImpl) write(blockBz []byte) error { + if b.poisoned { + return errors.New("poisoned") + } + + header := tar.Header{ + Name: fmt.Sprintf("%d", b.nextHeight), + Size: int64(len(blockBz)), + Mode: 0o664, + } + if err := b.w.WriteHeader(&header); err != nil { + b.poisoned = true + return fmt.Errorf("write tar file header: %w", err) + } + + if _, err := b.w.Write(blockBz); err != nil { + b.poisoned = true + return fmt.Errorf("write block body: %w", err) + } + + b.nextHeight += 1 + if b.nextHeight%ChunkSize != 1 { + return nil + } + + if err := b.finalizeChunk(); err != nil { + b.poisoned = true + return fmt.Errorf("finalize chunk: %w", err) + } + + b.chunkStart = b.nextHeight + + if err := b.openChunk(); err != nil { + b.poisoned = true + return fmt.Errorf("open chunk: %w", err) + } + + return nil +} + +func (b *writerImpl) openChunk() error { + var err error + + nextChunkFP := filepath.Join(b.dir, nextChunkFilename) + + b.outFile, err = os.OpenFile(nextChunkFP, os.O_RDWR|os.O_TRUNC|os.O_CREATE, 0o664) + if err != nil { + return err + } + + b.zstw, err = zstd.NewWriter(b.outFile) + if err != nil { + return errors.Join(b.outFile.Close(), err) + } + + b.w = tar.NewWriter(b.zstw) + + return nil +} + +func (b *writerImpl) finalizeChunk() error { + if err := b.closeWriters(); err != nil { + return err + } + + if b.nextHeight == b.chunkStart || b.poisoned { + return nil + } + + nextChunkFP := filepath.Join(b.dir, nextChunkFilename) + + // using padding for filename to match chunk order and lexicographical order + chunkFP := getChunkFP(b.dir, b.chunkStart) + if err := os.Rename(nextChunkFP, chunkFP); err != nil { + return err + } + + b.state.EndHeight = b.nextHeight - 1 + if err := b.state.save(); err != nil { + return err + } + + b.logger.Debug("Wrote chunk", zap.Int64("start", b.chunkStart), zap.Int64("end", b.nextHeight-1), zap.String("file", chunkFP)) + + return nil +} + +func (b *writerImpl) closeWriters() error { + errs := make([]error, 0, 3) + + if b.w != nil { + errs = append(errs, b.w.Close()) + b.w = nil + } + + if b.zstw != nil { + errs = append(errs, b.zstw.Close()) + b.zstw = nil + } + + if b.outFile != nil { + errs = append(errs, b.outFile.Close()) + b.outFile = nil + } + + return errors.Join(errs...) +} + +func getStartHeight(requestedStartHeight int64, backupHeight int64) (int64, error) { + height := int64(1) + + if requestedStartHeight != 0 && backupHeight != -1 { + return 0, errors.New("can't request a start height when resuming, use a different output directory or no start height") + } + + if requestedStartHeight != 0 { + height = requestedStartHeight + } else { + height = backupHeight + 1 + } + + // align: 4 -> 1, 100 -> 1, 101 -> 101, 150 -> 101 (with ChunkSize == 100) + // we simply overwrite the latest chunk if it is partial because it's not expensive + height -= (height - 1) % ChunkSize + + if height < 1 || height%ChunkSize != 1 { + return 0, fmt.Errorf("unexpected start height %d", height) + } + + return height, nil +} diff --git a/tm2/pkg/bft/blockchain/reactor.go b/tm2/pkg/bft/blockchain/reactor.go index 02714a7619e..7e7630453fa 100644 --- a/tm2/pkg/bft/blockchain/reactor.go +++ b/tm2/pkg/bft/blockchain/reactor.go @@ -1,6 +1,7 @@ package blockchain import ( + "context" "errors" "fmt" "log/slog" @@ -361,6 +362,82 @@ FOR_LOOP: } } +type BlocksIterator func(yield func(block *types.Block) error) error + +func (bcR *BlockchainReactor) Restore(ctx context.Context, blocksIterator BlocksIterator, skipVerification bool) error { + var ( + first *types.Block + second *types.Block + chainID = bcR.initialState.ChainID + state = bcR.initialState + err error + ) + + blockBatch := bcR.store.NewBatch() + blocksInBatch := 0 + batchSize := 1000 + saveBatch := func() error { + blockBatch.WriteSync() + err = blockBatch.Close() + if err != nil { + return err + } + + blocksInBatch = 0 + blockBatch = bcR.store.NewBatch() + return nil + } + return blocksIterator(func(block *types.Block) error { + defer func() { + if blocksInBatch > 0 { + err = saveBatch() + if err != nil { + panic(err) + } + } + }() + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + + if first == nil { + first = block + return nil + } + if blocksInBatch >= batchSize { + err = saveBatch() + if err != nil { + return err + } + } + + second = block + + firstParts := first.MakePartSet(types.BlockPartSizeBytes) + firstPartsHeader := firstParts.Header() + firstID := types.BlockID{Hash: first.Hash(), PartsHeader: firstPartsHeader} + if !skipVerification { + if err := state.Validators.VerifyCommit( + chainID, firstID, first.Height, second.LastCommit); err != nil { + return fmt.Errorf("invalid commit (%d:%X): %w", first.Height, first.Hash(), err) + } + } + + bcR.store.SaveBlockWithBatch(blockBatch, first, firstParts, second.LastCommit) + + state, err = bcR.blockExec.ApplyBlock(state, firstID, first) + if err != nil { + return fmt.Errorf("failed to process committed block (%d:%X): %w", first.Height, first.Hash(), err) + } + + first = second + blocksInBatch++ + return nil + }) +} + // BroadcastStatusRequest broadcasts `BlockStore` height. func (bcR *BlockchainReactor) BroadcastStatusRequest() error { msgBytes := amino.MustMarshalAny(&bcStatusRequestMessage{bcR.store.Height()}) diff --git a/tm2/pkg/bft/blockchain/reactor_test.go b/tm2/pkg/bft/blockchain/reactor_test.go index c3e55fce3ca..f38ab5b7648 100644 --- a/tm2/pkg/bft/blockchain/reactor_test.go +++ b/tm2/pkg/bft/blockchain/reactor_test.go @@ -25,6 +25,7 @@ import ( p2pTypes "github.com/gnolang/gno/tm2/pkg/p2p/types" "github.com/gnolang/gno/tm2/pkg/testutils" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) var config *cfg.Config @@ -390,6 +391,86 @@ func TestBcStatusResponseMessageValidateBasic(t *testing.T) { } } +func TestRestore(t *testing.T) { + t.Parallel() + + config, _ = cfg.ResetTestRoot("blockchain_reactor_test") + defer os.RemoveAll(config.RootDir) + genDoc, privVals := randGenesisDoc(1, false, 30) + + logger := log.NewNoopLogger() + + reactor := newBlockchainReactor(logger, genDoc, privVals, 0) + + stateDB := memdb.NewMemDB() + state, err := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc) + require.NoError(t, err) + + app := &testApp{} + cc := proxy.NewLocalClientCreator(app) + proxyApp := appconn.NewAppConns(cc) + require.NoError(t, proxyApp.Start()) + + // we generate blocks using another executor and then restore using the test reactor that has it's own executor + db := memdb.NewMemDB() + blockExec := sm.NewBlockExecutor(db, logger, proxyApp.Consensus(), mock.Mempool{}) + sm.SaveState(db, state) + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + var ( + lastBlock *types.Block + lastBlockMeta *types.BlockMeta + blockHeight int64 = 1 + ) + generateBlock := func() *types.Block { + t.Helper() + + lastCommit := types.NewCommit(types.BlockID{}, nil) + if blockHeight > 1 { + vote, err := types.MakeVote(lastBlock.Header.Height, lastBlockMeta.BlockID, state.Validators, privVals[0], lastBlock.Header.ChainID) + require.NoError(t, err) + voteCommitSig := vote.CommitSig() + lastCommit = types.NewCommit(lastBlockMeta.BlockID, []*types.CommitSig{voteCommitSig}) + } + + thisBlock := makeBlock(blockHeight, state, lastCommit) + + thisParts := thisBlock.MakePartSet(types.BlockPartSizeBytes) + blockID := types.BlockID{Hash: thisBlock.Hash(), PartsHeader: thisParts.Header()} + + state, err = blockExec.ApplyBlock(state, blockID, thisBlock) + require.NoError(t, err) + + lastBlock = thisBlock + lastBlockMeta = &types.BlockMeta{BlockID: blockID, Header: lastBlock.Header} + + blockHeight++ + + return thisBlock + } + + numBlocks := 50 + + err = reactor.reactor.Restore(ctx, func(yield func(block *types.Block) error) error { + for range numBlocks { + block := generateBlock() + + err := yield(block) + require.NoError(t, err) + + if blockHeight > 2 { + require.NotNil(t, reactor.reactor.store.LoadBlock(blockHeight-2)) + } + + require.Equal(t, blockHeight-2, reactor.reactor.store.Height()) + } + return nil + }, false) + require.NoError(t, err) +} + // ---------------------------------------------- // utility funcs diff --git a/tm2/pkg/bft/config/config.go b/tm2/pkg/bft/config/config.go index 29734a69a72..566621347cd 100644 --- a/tm2/pkg/bft/config/config.go +++ b/tm2/pkg/bft/config/config.go @@ -11,6 +11,7 @@ import ( "dario.cat/mergo" abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" + "github.com/gnolang/gno/tm2/pkg/bft/backup" cns "github.com/gnolang/gno/tm2/pkg/bft/consensus/config" mem "github.com/gnolang/gno/tm2/pkg/bft/mempool/config" rpc "github.com/gnolang/gno/tm2/pkg/bft/rpc/config" @@ -55,6 +56,7 @@ type Config struct { TxEventStore *eventstore.Config `json:"tx_event_store" toml:"tx_event_store" comment:"##### event store #####"` Telemetry *telemetry.Config `json:"telemetry" toml:"telemetry" comment:"##### node telemetry #####"` Application *sdk.AppConfig `json:"application" toml:"application" comment:"##### app settings #####"` + Backup *backup.Config `json:"backup" toml:"backup" comment:"##### backup server configuration options #####"` } // DefaultConfig returns a default configuration for a Tendermint node @@ -68,6 +70,7 @@ func DefaultConfig() *Config { TxEventStore: eventstore.DefaultEventStoreConfig(), Telemetry: telemetry.DefaultTelemetryConfig(), Application: sdk.DefaultAppConfig(), + Backup: backup.DefaultConfig(), } } diff --git a/tm2/pkg/bft/config/toml.go b/tm2/pkg/bft/config/toml.go index d35dd5ae682..70e4fee5dc4 100644 --- a/tm2/pkg/bft/config/toml.go +++ b/tm2/pkg/bft/config/toml.go @@ -5,6 +5,7 @@ import ( "os" "path/filepath" + "github.com/gnolang/gno/tm2/pkg/bft/backup" osm "github.com/gnolang/gno/tm2/pkg/os" "github.com/pelletier/go-toml" ) @@ -105,6 +106,11 @@ func ResetTestRoot(testName string) (*Config, string) { osm.MustWriteFile(config.Consensus.PrivValidator.LocalSignerPath(), []byte(testPrivValidatorKey), 0o644) osm.MustWriteFile(config.Consensus.PrivValidator.SignStatePath(), []byte(testPrivValidatorState), 0o644) + if config.Backup == nil { + config.Backup = &backup.Config{} + } + config.Backup.ListenAddress = "unix://" + filepath.Join(rootDir, "backup.sock") + return config, genesisFilePath } diff --git a/tm2/pkg/bft/node/node.go b/tm2/pkg/bft/node/node.go index 7f1fb8ad132..5c1fa4f851d 100644 --- a/tm2/pkg/bft/node/node.go +++ b/tm2/pkg/bft/node/node.go @@ -4,6 +4,7 @@ package node // is enabled by the user by setting a profiling address import ( + "context" "fmt" "log/slog" "net" @@ -15,6 +16,7 @@ import ( "github.com/rs/cors" "github.com/gnolang/gno/tm2/pkg/bft/appconn" + "github.com/gnolang/gno/tm2/pkg/bft/backup" "github.com/gnolang/gno/tm2/pkg/bft/privval" "github.com/gnolang/gno/tm2/pkg/bft/state/eventstore/file" "github.com/gnolang/gno/tm2/pkg/p2p/conn" @@ -168,9 +170,9 @@ type Node struct { // services evsw events.EventSwitch stateDB dbm.DB - blockStore *store.BlockStore // store the blockchain to disk - bcReactor p2p.Reactor // for fast-syncing - mempoolReactor *mempl.Reactor // for gossipping transactions + blockStore *store.BlockStore // store the blockchain to disk + bcReactor *bc.BlockchainReactor // for fast-syncing and restoring from backup + mempoolReactor *mempl.Reactor // for gossipping transactions mempool mempl.Mempool consensusState *cs.ConsensusState // latest consensus state consensusReactor *cs.ConsensusReactor // for participating in the consensus @@ -179,6 +181,7 @@ type Node struct { txEventStore eventstore.TxEventStore eventStoreService *eventstore.Service firstBlockSignal <-chan struct{} + backupServer *http.Server } func initDBs(config *cfg.Config, dbProvider DBProvider) (blockStore *store.BlockStore, stateDB dbm.DB, err error) { @@ -300,7 +303,7 @@ func createBlockchainReactor( fastSync bool, switchToConsensusFn bc.SwitchToConsensusFn, logger *slog.Logger, -) (bcReactor p2p.Reactor, err error) { +) (bcReactor *bc.BlockchainReactor, err error) { bcReactor = bc.NewBlockchainReactor( state.Copy(), blockExec, @@ -564,6 +567,10 @@ func NewNode(config *cfg.Config, return node, nil } +func (n *Node) Restore(ctx context.Context, blocksIterator bc.BlocksIterator, skipVerification bool) error { + return n.bcReactor.Restore(ctx, blocksIterator, skipVerification) +} + // OnStart starts the Node. It implements service.Service. func (n *Node) OnStart() error { now := tmtime.Now() @@ -593,6 +600,16 @@ func (n *Node) OnStart() error { n.rpcListeners = listeners } + // start backup server if requested + if n.config.Backup != nil && n.config.Backup.ListenAddress != "" { + n.backupServer = backup.NewServer(n.config.Backup, n.blockStore) + go func() { + if err := n.backupServer.ListenAndServe(); err != nil { + n.Logger.Info("Backup server stopped", "err", err) + } + }() + } + // Start the transport. // The listen address for the transport needs to be an address within reach of the machine NIC listenAddress := p2pTypes.NetAddressString(n.nodeKey.ID(), n.config.P2P.ListenAddress) @@ -669,6 +686,15 @@ func (n *Node) OnStop() { n.isListening = false + // stop the backup server if started + if n.backupServer != nil { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*3) + defer cancel() + if err := n.backupServer.Shutdown(ctx); err != nil { + n.Logger.Error("Error closing backup server", "err", err) + } + } + // finally stop the listeners / external services for _, l := range n.rpcListeners { n.Logger.Info("Closing rpc listener", "listener", l) diff --git a/tm2/pkg/bft/store/store.go b/tm2/pkg/bft/store/store.go index ec08ef1de03..a8c2f5b461c 100644 --- a/tm2/pkg/bft/store/store.go +++ b/tm2/pkg/bft/store/store.go @@ -49,6 +49,11 @@ func (bs *BlockStore) Height() int64 { return bs.height } +// NewBatch returns a new database batch for grouping block store writes. +func (bs *BlockStore) NewBatch() dbm.Batch { + return bs.db.NewBatch() +} + // LoadBlock returns the block with the given height. // If no block is found for that height, it returns nil. func (bs *BlockStore) LoadBlock(height int64) *types.Block { @@ -156,6 +161,16 @@ func (bs *BlockStore) LoadSeenCommit(height int64) *types.Commit { // we need this to reload the precommits to catch-up nodes to the // most recent height. Otherwise they'd stall at H-1. func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) { + batch := bs.NewBatch() + bs.SaveBlockWithBatch(batch, block, blockParts, seenCommit) + err := batch.WriteSync() + if err != nil { + panic(err) + } + batch.Close() +} + +func (bs *BlockStore) SaveBlockWithBatch(batch dbm.Batch, block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) { if block == nil { panic("BlockStore can only save a non-nil block") } @@ -170,41 +185,38 @@ func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, s // Save block meta blockMeta := types.NewBlockMeta(block, blockParts) metaBytes := amino.MustMarshal(blockMeta) - bs.db.Set(calcBlockMetaKey(height), metaBytes) + batch.Set(calcBlockMetaKey(height), metaBytes) // Save block parts for i := range blockParts.Total() { part := blockParts.GetPart(i) - bs.saveBlockPart(height, i, part) + bs.saveBlockPart(batch, height, i, part) } // Save block commit (duplicate and separate from the Block) blockCommitBytes := amino.MustMarshal(block.LastCommit) - bs.db.Set(calcBlockCommitKey(height-1), blockCommitBytes) + batch.Set(calcBlockCommitKey(height-1), blockCommitBytes) // Save seen commit (seen +2/3 precommits for block) // NOTE: we can delete this at a later height seenCommitBytes := amino.MustMarshal(seenCommit) - bs.db.Set(calcSeenCommitKey(height), seenCommitBytes) + batch.Set(calcSeenCommitKey(height), seenCommitBytes) // Save new BlockStoreStateJSON descriptor - BlockStoreStateJSON{Height: height}.Save(bs.db) + BlockStoreStateJSON{Height: height}.Save(batch) // Done! bs.mtx.Lock() bs.height = height bs.mtx.Unlock() - - // Flush - bs.db.SetSync(nil, nil) } -func (bs *BlockStore) saveBlockPart(height int64, index int, part *types.Part) { +func (bs *BlockStore) saveBlockPart(batch dbm.Batch, height int64, index int, part *types.Part) { if height != bs.Height()+1 { panic(fmt.Sprintf("BlockStore can only save contiguous blocks. Wanted %v, got %v", bs.Height()+1, height)) } partBytes := amino.MustMarshal(part) - bs.db.Set(calcBlockPartKey(height, index), partBytes) + batch.Set(calcBlockPartKey(height, index), partBytes) } //----------------------------------------------------------------------------- @@ -235,12 +247,12 @@ type BlockStoreStateJSON struct { } // Save persists the blockStore state to the database as JSON. -func (bsj BlockStoreStateJSON) Save(db dbm.DB) { +func (bsj BlockStoreStateJSON) Save(batch dbm.Batch) { bytes, err := amino.MarshalJSON(bsj) if err != nil { panic(fmt.Sprintf("Could not marshal state bytes: %v", err)) } - db.SetSync(blockStoreKey, bytes) + batch.Set(blockStoreKey, bytes) } // LoadBlockStoreStateJSON returns the BlockStoreStateJSON as loaded from disk. diff --git a/tm2/pkg/bft/store/store_test.go b/tm2/pkg/bft/store/store_test.go index f3cd14676d7..867dafefd65 100644 --- a/tm2/pkg/bft/store/store_test.go +++ b/tm2/pkg/bft/store/store_test.go @@ -62,9 +62,12 @@ func TestLoadBlockStoreStateJSON(t *testing.T) { t.Parallel() db := memdb.NewMemDB() + batch := db.NewBatch() bsj := &BlockStoreStateJSON{Height: 1000} - bsj.Save(db) + bsj.Save(batch) + batch.WriteSync() + batch.Close() retrBSJ := LoadBlockStoreStateJSON(db)