From 6b24de9ade51ee15867590e2407acb2f8bcd4e58 Mon Sep 17 00:00:00 2001 From: Matheus Degiovani Date: Thu, 21 Dec 2023 12:27:02 -0300 Subject: [PATCH] integration: Initial version of tests. This adds integration tests that assert the correct behavior of restoring a wallet under various scenarios. In particular, both SPV and RPC syncers and both legacy and SLIP0044 wallets are tested under scenarios that were known to cause missed transactions. --- go.mod | 32 ++- go.sum | 166 ++++++------ internal/integration/mock_test.go | 364 +++++++++++++++++++++++++++ internal/integration/restore_test.go | 292 +++++++++++++++++++++ internal/integration/util_test.go | 89 +++++++ 5 files changed, 842 insertions(+), 101 deletions(-) create mode 100644 internal/integration/mock_test.go create mode 100644 internal/integration/restore_test.go create mode 100644 internal/integration/util_test.go diff --git a/go.mod b/go.mod index 0b2f26823..eed6338fb 100644 --- a/go.mod +++ b/go.mod @@ -20,10 +20,13 @@ require ( github.com/decred/dcrd/dcrutil/v4 v4.0.1 github.com/decred/dcrd/gcs/v4 v4.0.0 github.com/decred/dcrd/hdkeychain/v3 v3.1.1 + github.com/decred/dcrd/peer/v3 v3.0.2 github.com/decred/dcrd/rpc/jsonrpc/types/v4 v4.1.0 github.com/decred/dcrd/rpcclient/v8 v8.0.0 github.com/decred/dcrd/txscript/v4 v4.1.0 github.com/decred/dcrd/wire v1.6.0 + github.com/decred/dcrtest/dcrdtest v1.0.0 + github.com/decred/dcrtest/dcrwtest v1.0.0 github.com/decred/go-socks v1.1.0 github.com/decred/slog v1.2.0 github.com/decred/vspd/client/v3 v3.0.0 @@ -35,25 +38,38 @@ require ( github.com/jrick/wsrpc/v2 v2.3.5 go.etcd.io/bbolt v1.3.8 golang.org/x/crypto v0.7.0 - golang.org/x/sync v0.3.0 - golang.org/x/term v0.6.0 - google.golang.org/grpc v1.45.0 + golang.org/x/sync v0.5.0 + golang.org/x/term v0.7.0 + google.golang.org/grpc v1.54.0 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0 - google.golang.org/protobuf v1.27.1 + google.golang.org/protobuf v1.30.0 + matheusd.com/testctx v0.2.0 ) +replace github.com/decred/dcrtest/dcrwtest => github.com/matheusd/dcrtest/dcrwtest v0.0.0-20231221151844-30516a66b790 + require ( + decred.org/dcrwallet/v3 v3.0.0 // indirect github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect github.com/companyzero/sntrup4591761 v0.0.0-20220309191932-9e0f3af2f07a // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/dchest/siphash v1.2.3 // indirect github.com/decred/base58 v1.0.5 // indirect + github.com/decred/dcrd v1.8.0 // indirect + github.com/decred/dcrd/bech32 v1.1.3 // indirect + github.com/decred/dcrd/container/apbf v1.0.1 // indirect github.com/decred/dcrd/database/v3 v3.0.1 // indirect github.com/decred/dcrd/dcrec/edwards/v2 v2.0.3 // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/decred/dcrd/lru v1.1.2 // indirect + github.com/decred/dcrd/math/uint256 v1.0.1 // indirect + github.com/decred/vspd/client/v2 v2.0.0 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/snappy v0.0.4 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect - golang.org/x/net v0.8.0 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect + golang.org/x/net v0.9.0 // indirect golang.org/x/sys v0.12.0 // indirect - golang.org/x/text v0.8.0 // indirect - google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect + golang.org/x/text v0.9.0 // indirect + google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect lukechampine.com/blake3 v1.2.1 // indirect ) diff --git a/go.sum b/go.sum index 909699e8f..00c6a32ce 100644 --- a/go.sum +++ b/go.sum @@ -1,30 +1,23 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= decred.org/cspp/v2 v2.1.0 h1:HeHb9+BFqrBaAPc6CsPiUpPFmC1uyBM2mJZUAbUXkRw= decred.org/cspp/v2 v2.1.0/go.mod h1:9nO3bfvCheOPIFZw5f6sRQ42CjBFB5RKSaJ9Iq6G4MA= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +decred.org/dcrwallet/v3 v3.0.0 h1:EdI7D9U7fnvfoWexWvlhPNri+Ws9RdvjP0A5Sc38TWs= +decred.org/dcrwallet/v3 v3.0.0/go.mod h1:a+R8BZIOKVpWVPat5VZoBWNh/cnIciwcRkPtrzfS/tw= github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 h1:w1UutsfOrms1J05zt7ISrnJIXKzwaspym5BTKGx93EI= github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/companyzero/sntrup4591761 v0.0.0-20220309191932-9e0f3af2f07a h1:clYxJ3Os0EQUKDDVU8M0oipllX0EkuFNBfhVQuIfyF0= github.com/companyzero/sntrup4591761 v0.0.0-20220309191932-9e0f3af2f07a/go.mod h1:z/9Ck1EDixEbBbZ2KH2qNHekEmDLTOZ+FyoIPWWSVOI= -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/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA= github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc= github.com/decred/base58 v1.0.5 h1:hwcieUM3pfPnE/6p3J100zoRfGkQxBulZHo7GZfOqic= github.com/decred/base58 v1.0.5/go.mod h1:s/8lukEHFA6bUQQb/v3rjUySJ2hu+RioCzLukAVkrfw= +github.com/decred/dcrd v1.8.0 h1:aIqX3VnY9CCcOwI5kskGTw+LH5B4Am+6kjfnN/xkyks= +github.com/decred/dcrd v1.8.0/go.mod h1:s8ccr5TraoSCGorOTpnludpwfKqEI8cjoAbmylbb6uw= github.com/decred/dcrd/addrmgr/v2 v2.0.2 h1:h7PF1FoWcGUBcOhon7hK4Du7gT4KJb2/dCC4SVVnvgI= github.com/decred/dcrd/addrmgr/v2 v2.0.2/go.mod h1:lMupOhByAzVJN7EFWSGLeGTrlvvx38uCY4D+bPf2AT4= +github.com/decred/dcrd/bech32 v1.1.3 h1:EeipVC1dO4zkjTjyqvrWt6JT2Ajr1EHZt+BAmWN864s= +github.com/decred/dcrd/bech32 v1.1.3/go.mod h1:jliqHZmCbVfT06Lh1mQywEKFVidRclbBJIUmwdoKhu0= github.com/decred/dcrd/blockchain/stake/v5 v5.0.0 h1:WyxS8zMvTMpC5qYC9uJY+UzuV/x9ko4z20qBtH5Hzzs= github.com/decred/dcrd/blockchain/stake/v5 v5.0.0/go.mod h1:5sSjMq9THpnrLkW0SjEqIBIo8qq2nXzc+m7k9oFVVmY= github.com/decred/dcrd/blockchain/standalone/v2 v2.2.0 h1:v3yfo66axjr3oLihct+5tLEeM9YUzvK3i/6e2Im6RO0= @@ -39,6 +32,8 @@ github.com/decred/dcrd/chaincfg/v3 v3.2.0 h1:6WxA92AGBkycEuWvxtZMvA76FbzbkDRoK8O github.com/decred/dcrd/chaincfg/v3 v3.2.0/go.mod h1:2rHW1TKyFmwZTVBLoU/Cmf0oxcpBjUEegbSlBfrsriI= github.com/decred/dcrd/connmgr/v3 v3.1.1 h1:si7bgYlyeSbB0Ewe+bfoO/RAzxuPwPkL40DDZhyITmo= github.com/decred/dcrd/connmgr/v3 v3.1.1/go.mod h1:YlRGPagi/6SJbG9CFq2ZnorX9+deRNb6+m0ovkNDcKY= +github.com/decred/dcrd/container/apbf v1.0.1 h1:oepQzRtLADudsrx0AmmowoU1kambozINTMXduH6Mge0= +github.com/decred/dcrd/container/apbf v1.0.1/go.mod h1:paQplssZMsRhwOUcP6LDNDypyb7lwsFHrUKUkAPFWtQ= 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/crypto/ripemd160 v1.0.2 h1:TvGTmUBHDU75OHro9ojPLK+Yv7gDl2hnUvRocRCjsys= @@ -59,6 +54,12 @@ github.com/decred/dcrd/gcs/v4 v4.0.0 h1:bet+Ax1ZFUqn2M0g1uotm0b8F6BZ9MmblViyJ088 github.com/decred/dcrd/gcs/v4 v4.0.0/go.mod h1:9z+EBagzpEdAumwS09vf/hiGaR8XhNmsBgaVq6u7/NI= github.com/decred/dcrd/hdkeychain/v3 v3.1.1 h1:4WhyHNBy7ec6qBUC7Fq7JFVGSd7bpuR5H+AJRID8Lyk= github.com/decred/dcrd/hdkeychain/v3 v3.1.1/go.mod h1:HaabrLc27lnny5/Ph9+6I3szp0op5MCb7smEwlzfD60= +github.com/decred/dcrd/lru v1.1.2 h1:KdCzlkxppuoIDGEvCGah1fZRicrDH36IipvlB1ROkFY= +github.com/decred/dcrd/lru v1.1.2/go.mod h1:gEdCVgXs1/YoBvFWt7Scgknbhwik3FgVSzlnCcXL2N8= +github.com/decred/dcrd/math/uint256 v1.0.1 h1:SUMHmqi4GQwBNdueS5Pjj6Pzr3fZrc31skpSuEb6e64= +github.com/decred/dcrd/math/uint256 v1.0.1/go.mod h1:7M/y9wJJvlyNG/f/X6mxxhxo9dgloZHFiOfbiscl75A= +github.com/decred/dcrd/peer/v3 v3.0.2 h1:akcB/L5tZcV/LV5LsiAAeShzvus8mjwvkLI20b1aSUM= +github.com/decred/dcrd/peer/v3 v3.0.2/go.mod h1:J3Wwi3biKzsIoQS61LnpFyjDgP7oigdoptf2r6yNMBQ= github.com/decred/dcrd/rpc/jsonrpc/types/v4 v4.1.0 h1:kQFK7FMTmMDX9amyhh8IR0vwwI8dH0KCBm42C64bWVs= github.com/decred/dcrd/rpc/jsonrpc/types/v4 v4.1.0/go.mod h1:dDHO7ivrPAhZjFD3LoOJN/kdq5gi0sxie6zCsWHAiUo= github.com/decred/dcrd/rpcclient/v8 v8.0.0 h1:O4B5d+8e2OjbeFW+c1XcZNQzyp++04ArWhXgYrsURus= @@ -67,49 +68,41 @@ github.com/decred/dcrd/txscript/v4 v4.1.0 h1:uEdcibIOl6BuWj3AqmXZ9xIK/qbo6lHY9aN github.com/decred/dcrd/txscript/v4 v4.1.0/go.mod h1:OVguPtPc4YMkgssxzP8B6XEMf/J3MB6S1JKpxgGQqi0= github.com/decred/dcrd/wire v1.6.0 h1:YOGwPHk4nzGr6OIwUGb8crJYWDiVLpuMxfDBCCF7s/o= github.com/decred/dcrd/wire v1.6.0/go.mod h1:XQ8Xv/pN/3xaDcb7sH8FBLS9cdgVctT7HpBKKGsIACk= +github.com/decred/dcrtest/dcrdtest v1.0.0 h1:M50BpCPwkCJLtRa2MAo7QMs9sQFgZdCjTnZahnak594= +github.com/decred/dcrtest/dcrdtest v1.0.0/go.mod h1:kpuTg2gpr7ze14cumpsLa2ecRC1M4mt36IrRKzNRipU= github.com/decred/go-socks v1.1.0 h1:dnENcc0KIqQo3HSXdgboXAHgqsCIutkqq6ntQjYtm2U= github.com/decred/go-socks v1.1.0/go.mod h1:sDhHqkZH0X4JjSa02oYOGhcGHYp12FsY1jQ/meV8md0= github.com/decred/slog v1.2.0 h1:soHAxV52B54Di3WtKLfPum9OFfWqwtf/ygf9njdfnPM= github.com/decred/slog v1.2.0/go.mod h1:kVXlGnt6DHy2fV5OjSeuvCJ0OmlmTF6LFpEPMu/fOY0= +github.com/decred/vspd/client/v2 v2.0.0 h1:gaSF1Bm2/EvoAiSLxNR5fgStrObAO66xmanhedidYIM= +github.com/decred/vspd/client/v2 v2.0.0/go.mod h1:IDDviEe/6CuxxrW0PLOcg448enU3YmeElFHledYHw78= github.com/decred/vspd/client/v3 v3.0.0 h1:4gAGDTeIU0r4quCxmV5Ez7T2J+P+OLPSibkCF+/Yb6w= github.com/decred/vspd/client/v3 v3.0.0/go.mod h1:5pfPvIa6V38AmophMrKUCl3KMpEIxcltWtgL2R+wsW8= github.com/decred/vspd/types/v2 v2.1.0 h1:cUVlmHPeLVsksPRnr2WHsmC2t1Skl6g1WH0HmpcPS7w= github.com/decred/vspd/types/v2 v2.1.0/go.mod h1:2xnNqedkt9GuL+pK8uIzDxqYxFlwLRflYFJH64b76n0= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 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.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +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.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jrick/bitset v1.0.0 h1:Ws0PXV3PwXqWK2n7Vz6idCdrV/9OrBXgHEJi27ZB9Dw= @@ -120,78 +113,64 @@ github.com/jrick/wsrpc/v2 v2.3.5 h1:CwdycaR/df09iGkPMXs1FxqAHMCQbdAiTGoHfOrtuds= github.com/jrick/wsrpc/v2 v2.3.5/go.mod h1:7oBeDM/xMF6Yqy4GDAjpppuOf1hm6lWsaG3EaMrm+aA= github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/matheusd/dcrtest/dcrwtest v0.0.0-20231221151844-30516a66b790 h1:41a/IRf4PeX4l50SaAP0tESTstZGXbuap1YPrLLPvKo= +github.com/matheusd/dcrtest/dcrwtest v0.0.0-20231221151844-30516a66b790/go.mod h1:woCpUjhXTcq981B5AKRoVvVetBZb3jAgsx7wSWOSLB0= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= 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.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-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-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +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.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +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.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +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.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= +google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= +google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= +google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0 h1:TLkBREm4nIsEcexnCjgQd5GQWaHcqMzwQV0TX9pq8S0= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0/go.mod h1:DNq5QpG7LJqD2AamLZ7zvKE0DEpVl2BSEVjFycAAjRY= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -199,20 +178,21 @@ google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ 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.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= +matheusd.com/testctx v0.2.0 h1:nqf6TGz6hbhVQHtBooegvyB5UHcWMioMRPUrYSa9ypI= +matheusd.com/testctx v0.2.0/go.mod h1:u9la0YA1XIBcEpTU/aHJ9q4/L0VttkwhkG2m4lrj7Ls= diff --git a/internal/integration/mock_test.go b/internal/integration/mock_test.go new file mode 100644 index 000000000..2a1aa5acd --- /dev/null +++ b/internal/integration/mock_test.go @@ -0,0 +1,364 @@ +package integration + +import ( + "bytes" + "context" + "encoding/hex" + "encoding/json" + "fmt" + "net" + "net/http" + "sync" + "testing" + "time" + + "github.com/decred/dcrd/chaincfg/chainhash" + "github.com/decred/dcrd/dcrjson/v4" + "github.com/decred/dcrd/peer/v3" + "github.com/decred/dcrd/rpc/jsonrpc/types/v4" + "github.com/decred/dcrd/rpcclient/v8" + "github.com/decred/dcrd/wire" + "github.com/gorilla/websocket" + "golang.org/x/sync/errgroup" +) + +type syncerProxy interface { + blockCFiltersAfter(height uint32) + run(ctx context.Context) error + blocked() <-chan struct{} +} + +type spvSyncerProxy struct { + mu sync.Mutex + inListener net.Listener + cfBlockHeight uint32 + dcrd *rpcclient.Client + blockedChan chan struct{} +} + +func newSPVSyncerProxy(t testing.TB, rpcCfg rpcclient.ConnConfig) *spvSyncerProxy { + inListener, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + + rpcClient, err := rpcclient.New(&rpcCfg, nil) + if err != nil { + t.Fatal(err) + } + + return &spvSyncerProxy{ + inListener: inListener, + dcrd: rpcClient, + blockedChan: make(chan struct{}), + } +} + +func (proxy *spvSyncerProxy) addr() string { + return proxy.inListener.Addr().String() +} + +func (proxy *spvSyncerProxy) blockCFiltersAfter(height uint32) { + proxy.cfBlockHeight = height +} + +func (proxy *spvSyncerProxy) blocked() <-chan struct{} { + return proxy.blockedChan +} + +func (proxy *spvSyncerProxy) run(ctx context.Context) error { + dcrd := proxy.dcrd + + inPeerCfg := peer.Config{ + UserAgentName: "peer", + UserAgentVersion: "1.0.0", + Net: wire.SimNet, + Services: wire.SFNodeNetwork | wire.SFNodeCF, + IdleTimeout: time.Second * 120, + NewestBlock: func() (hash *chainhash.Hash, height int64, err error) { + return dcrd.GetBestBlock(ctx) + }, + Listeners: peer.MessageListeners{ + OnGetInitState: func(p *peer.Peer, msg *wire.MsgGetInitState) { + p.QueueMessage(&wire.MsgInitState{}, nil) + }, + OnPing: func(p *peer.Peer, msg *wire.MsgPing) { + p.QueueMessage(&wire.MsgPong{Nonce: msg.Nonce}, nil) + }, + OnGetHeaders: func(p *peer.Peer, msg *wire.MsgGetHeaders) { + res, err := dcrd.GetHeaders(ctx, msg.BlockLocatorHashes, &msg.HashStop) + if err != nil { + return + } + nbHeaders := 100 + if len(res.Headers) < nbHeaders { + nbHeaders = len(res.Headers) + } + resMsg := &wire.MsgHeaders{ + Headers: make([]*wire.BlockHeader, nbHeaders), + } + for i := 0; i < nbHeaders; i++ { + b, _ := hex.DecodeString(res.Headers[i]) + resMsg.Headers[i] = new(wire.BlockHeader) + resMsg.Headers[i].Deserialize(bytes.NewBuffer(b)) + } + p.QueueMessage(resMsg, nil) + }, + OnGetCFilterV2: func(p *peer.Peer, msg *wire.MsgGetCFilterV2) { + // Do not return cf if instructed to block. + header, err := dcrd.GetBlockHeader(ctx, &msg.BlockHash) + if err != nil { + return + } + if proxy.cfBlockHeight > 0 && header.Height >= proxy.cfBlockHeight { + proxy.mu.Lock() + select { + case <-proxy.blockedChan: + default: + close(proxy.blockedChan) + } + proxy.mu.Unlock() + return + } + + // Return cfilter. + res, err := dcrd.GetCFilterV2(ctx, &msg.BlockHash) + if err != nil { + return + } + resMsg := &wire.MsgCFilterV2{ + BlockHash: msg.BlockHash, + Data: res.Filter.Bytes(), + ProofIndex: res.ProofIndex, + ProofHashes: res.ProofHashes, + } + p.QueueMessage(resMsg, nil) + }, + OnGetData: func(p *peer.Peer, msg *wire.MsgGetData) { + var resMsg wire.Message + for _, inv := range msg.InvList { + switch inv.Type { + case wire.InvTypeBlock: + res, err := dcrd.GetBlock(ctx, &inv.Hash) + if err != nil { + return + } + resMsg = res + } + } + if resMsg != nil { + p.QueueMessage(resMsg, nil) + } + }, + }, + } + + go func() { + <-ctx.Done() + proxy.inListener.Close() + }() + + for ctx.Err() == nil { + // Wait until the wallet connects. + inConn, err := proxy.inListener.Accept() + if err != nil { + return err + } + inPeer := peer.NewInboundPeer(&inPeerCfg) + inPeer.AssociateConnection(inConn) + } + return ctx.Err() +} + +type rpcSyncerProxy struct { + mu sync.Mutex + inListener net.Listener + cfBlockHeight uint32 + dcrd *rpcclient.Client + blockedChan chan struct{} +} + +func newRPCSyncerProxy(t testing.TB, rpcCfg rpcclient.ConnConfig) *rpcSyncerProxy { + inListener, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + + rpcClient, err := rpcclient.New(&rpcCfg, nil) + if err != nil { + t.Fatal(err) + } + + proxy := &rpcSyncerProxy{ + inListener: inListener, + dcrd: rpcClient, + blockedChan: make(chan struct{}), + } + + return proxy +} + +func (proxy *rpcSyncerProxy) connConfig() rpcclient.ConnConfig { + return rpcclient.ConnConfig{ + Host: proxy.inListener.Addr().String(), + Endpoint: "ws", + DisableTLS: true, + User: "none", + Pass: "none", + } +} + +func (proxy *rpcSyncerProxy) blockCFiltersAfter(height uint32) { + proxy.cfBlockHeight = height +} + +func (proxy *rpcSyncerProxy) blocked() <-chan struct{} { + return proxy.blockedChan +} + +func (proxy *rpcSyncerProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/ws" { + return + } + + upgrader := websocket.Upgrader{} + ws, err := upgrader.Upgrade(w, r, nil) + if err != nil { + fmt.Println("Upgrade err", err) + return + } + ws.SetPingHandler(func(payload string) error { + return ws.WriteControl(websocket.PongMessage, []byte(payload), + time.Time{}) + }) + ws.SetReadDeadline(time.Time{}) + ctx := r.Context() + + var g errgroup.Group + g.Go(func() error { + for ctx.Err() == nil { + _, msg, err := ws.ReadMessage() + if err != nil { + return fmt.Errorf("ReadMessage err %v", err) + } + + var req dcrjson.Request + var result any + err = json.Unmarshal(msg, &req) + if err != nil { + return fmt.Errorf("Unmarshal err %v", err) + } + + method := types.Method(req.Method) + params, err := dcrjson.ParseParams(method, req.Params) + if err != nil { + fmt.Println("ParseParams err", err) + } + + switch req.Method { + case "getcurrentnet": + result = wire.SimNet + case "version": + result = map[string]types.VersionResult{ + // This will require a bump every time the + // major json rpc version changes. + "dcrdjsonrpcapi": {Major: 8, Minor: 99999, Patch: 99999}, + } + case "getblockchaininfo": + result, err = proxy.dcrd.GetBlockChainInfo(ctx) + if err != nil { + return fmt.Errorf("GetBlockChainInfo err %v", err) + } + + case "getinfo": + result, err = proxy.dcrd.GetInfo(ctx) + if err != nil { + return err + } + + case "getheaders": + c := params.(*types.GetHeadersCmd) + locators, err := decodeHashes(c.BlockLocators) + if err != nil { + return err + } + var hashStop chainhash.Hash + if c.HashStop != "" { + err := chainhash.Decode(&hashStop, c.HashStop) + if err != nil { + return err + } + } + + res, err := proxy.dcrd.GetHeaders(ctx, locators, &hashStop) + if err != nil { + return err + } + + if len(res.Headers) > 100 { + res.Headers = res.Headers[:100] + } + result = res + + case "getcfilterv2": + c := params.(*types.GetCFilterV2Cmd) + hash, err := chainhash.NewHashFromStr(c.BlockHash) + if err != nil { + return fmt.Errorf("GetCFilterV2 hash err %v", err) + } + + header, err := proxy.dcrd.GetBlockHeader(ctx, hash) + if err != nil { + return err + } + if proxy.cfBlockHeight > 0 && header.Height >= proxy.cfBlockHeight { + proxy.mu.Lock() + select { + case <-proxy.blockedChan: + default: + close(proxy.blockedChan) + } + proxy.mu.Unlock() + } else { + res, err := proxy.dcrd.GetCFilterV2(ctx, hash) + if err != nil { + return fmt.Errorf("GetCFilterV2 err %v", err) + } + result = &types.GetCFilterV2Result{ + BlockHash: res.BlockHash.String(), + Data: hex.EncodeToString(res.Filter.Bytes()), + ProofIndex: res.ProofIndex, + ProofHashes: encodeHashes(res.ProofHashes), + } + } + + default: + return fmt.Errorf("unhandled method %s\n", req.Method) + } + + if result == nil { + continue + } + + reply, err := dcrjson.MarshalResponse(req.Jsonrpc, req.ID, result, nil) + if err != nil { + return fmt.Errorf("MarhsalResponse err %v", err) + } + err = ws.WriteMessage(websocket.TextMessage, reply) + if err != nil { + return fmt.Errorf("WriteMessage err %v", err) + } + } + return ctx.Err() + }) + _ = g.Wait() + _ = ws.Close() +} + +func (proxy *rpcSyncerProxy) run(ctx context.Context) error { + srv := &http.Server{Handler: proxy} + srv.Serve(proxy.inListener) + <-ctx.Done() + srv.Close() + return ctx.Err() +} diff --git a/internal/integration/restore_test.go b/internal/integration/restore_test.go new file mode 100644 index 000000000..2a39eb01a --- /dev/null +++ b/internal/integration/restore_test.go @@ -0,0 +1,292 @@ +package integration + +import ( + "context" + "fmt" + "math/rand" + "strconv" + "strings" + "testing" + "time" + + "decred.org/dcrwallet/v4/wallet/txrules" + "github.com/decred/dcrd/chaincfg/chainhash" + "github.com/decred/dcrd/chaincfg/v3" + "github.com/decred/dcrd/dcrutil/v4" + "github.com/decred/dcrd/hdkeychain/v3" + "github.com/decred/dcrd/txscript/v4/stdaddr" + "github.com/decred/dcrd/wire" + "github.com/decred/dcrtest/dcrdtest" + "github.com/decred/dcrtest/dcrwtest" + "matheusd.com/testctx" +) + +type testHarness struct { + testing.TB + vw *dcrdtest.VotingWallet + h *dcrdtest.Harness + params *chaincfg.Params +} + +func (t *testHarness) mine(nbBlocks int) []*chainhash.Hash { + t.Helper() + res, err := t.vw.GenerateBlocks(testctx.New(t), uint32(nbBlocks)) + if err != nil { + t.Fatal(err) + } + return res +} + +func (t *testHarness) assertBlockIncludesTx(block, tx *chainhash.Hash) { + t.Helper() + b, err := t.h.Node.GetBlock(testctx.New(t), block) + if err != nil { + t.Fatal(err) + } + for _, txh := range b.Transactions { + if txh.TxHash() == *tx { + return + } + } + t.Fatalf("block %s does not include tx %s", block, tx) +} + +func (t *testHarness) assertCumulativeBalance(w *dcrwtest.Wallet, wantBalance dcrutil.Amount) { + t.Helper() + wbal, err := jrpcClient(t, w).GetBalance(testctx.New(t), "*") + if err != nil { + t.Fatal(err) + } + + gotBalance, err := dcrutil.NewAmount(wbal.CumulativeTotal) + if err != nil { + t.Fatal(err) + } + + if gotBalance != wantBalance { + t.Fatalf("Unexpected balance: got %v, want %v", gotBalance, wantBalance) + } +} + +func (t *testHarness) waitWalletHeight(ctx context.Context, w *dcrwtest.Wallet, targetHeight int32) { + for { + info, err := jrpcClient(t, w).GetInfo(ctx) + if err == nil && info.Blocks >= targetHeight { + return + } + + select { + case <-ctx.Done(): + t.Fatal("ctx.Done while waiting target height") + case <-time.After(10 * time.Millisecond): + } + } +} + +func (t *testHarness) sendTo(addr string, amount dcrutil.Amount) *chainhash.Hash { + t.Helper() + saddr, err := stdaddr.DecodeAddress(addr, t.params) + if err != nil { + t.Fatal(err) + } + version, pkScript := saddr.PaymentScript() + out := wire.NewTxOut(int64(amount), pkScript) + out.Version = version + hash, err := t.h.SendOutputs(testctx.New(t), []*wire.TxOut{out}, txrules.DefaultRelayFeePerKb) + if err != nil { + t.Fatal(err) + } + return hash +} + +func (t *testHarness) addrFromSeed(seed []byte, path string) stdaddr.Address { + t.Helper() + key, err := hdkeychain.NewMaster(seed, t.params) + if err != nil { + t.Fatal(err) + } + + split := strings.Split(path, "/") + ipath := make([]uint32, len(split)) + for i, s := range split { + if s[len(s)-1] == '\'' { + ipath[i] = hdkeychain.HardenedKeyStart + s = s[:len(s)-1] + } + v, err := strconv.ParseUint(s, 10, 32) + if err != nil { + t.Fatal(err) + } + ipath[i] += uint32(v) + } + for i := range ipath { + var child *hdkeychain.ExtendedKey + for child == nil { + child, _ = key.Child(ipath[i]) + ipath[i] += 1 + } + key = child + } + + addrPK, err := stdaddr.NewAddressPubKeyEcdsaSecp256k1V0Raw(key.SerializedPubKey(), t.params) + if err != nil { + t.Fatal(err) + } + addrPKH := addrPK.AddressPubKeyHash() + return addrPKH +} + +type restoreScenario struct { + name string + walletAddrPath string + syncOpt string + createOpt string + wantBalance bool +} + +func testRestore(t *testHarness, rs *restoreScenario) { + var syncOpt, syncOptWithProxy dcrwtest.Opt + var proxy syncerProxy + switch rs.syncOpt { + case "rpc": + syncOpt = dcrwtest.WithRPCSync(t.h.RPCConfig()) + rpcProxy := newRPCSyncerProxy(t, t.h.RPCConfig()) + proxy = rpcProxy + syncOptWithProxy = dcrwtest.WithRPCSync(rpcProxy.connConfig()) + case "spv": + syncOpt = dcrwtest.WithSPVSync(t.h.P2PAddress()) + spvProxy := newSPVSyncerProxy(t, t.h.RPCConfig()) + proxy = spvProxy + syncOptWithProxy = dcrwtest.WithSPVSync(spvProxy.addr()) + default: + panic("unknown syncOpt") + } + + var createOpt, syncMethod dcrwtest.Opt + switch rs.createOpt { + case "stdio": + createOpt = dcrwtest.WithCreateUsingStdio() + syncMethod = dcrwtest.WithSyncUsingJsonRPC() + case "grpc": + createOpt = dcrwtest.WithCreateUsingGRPC() + syncMethod = dcrwtest.WithSyncUsingGRPC() + default: + panic("unknown createOpt") + } + + const sendAmount dcrutil.Amount = 1e8 + + // Generate a seed. + var seed [32]byte + rand.Read(seed[:]) + + // Send funds to a wallet address that should be found after restore. + addr := t.addrFromSeed(seed[:], rs.walletAddrPath) + tx := t.sendTo(addr.String(), sendAmount) + bl := t.mine(1)[0] + t.assertBlockIncludesTx(bl, tx) + + // First test: restore with tx in tip block. + opts := []dcrwtest.Opt{ + dcrwtest.WithDebugLevel("trace"), + dcrwtest.WithHDSeed(seed[:]), + syncOpt, + createOpt, + syncMethod, + // dcrwtest.WithExposeCmdOutput(), + } + w := dcrwtest.RunForTest(testctx.New(t), t, t.params, opts...) + time.Sleep(2 * time.Second) // FIXME: remove after :2317 is fixed. + t.assertCumulativeBalance(w, sendAmount) + + // Second test: restore with tx deeper in history. + t.mine(2) + w = dcrwtest.RunForTest(testctx.New(t), t, t.params, opts...) + time.Sleep(2 * time.Second) // FIXME: remove after :2317 is fixed. + t.assertCumulativeBalance(w, sendAmount) + + // Third test: Stop wallet halfway through syncing (before block with + // tx) then restart. Create the wallet and run it using a proxy syncer + // that does not return blocks after block 10. Then stop and restart + // the wallet without the proxy blocker. + proxy.blockCFiltersAfter(101) + ctx, cancel := testctx.WithCancel(t) + go proxy.run(ctx) + optsWithProxy := append(opts, syncOptWithProxy, dcrwtest.WithNoWaitInitialSync()) + w = dcrwtest.RunForTest(ctx, t, t.params, optsWithProxy...) + select { + case <-proxy.blocked(): + case <-ctx.Done(): + t.Fatal("never blocked any CFs") + } + t.waitWalletHeight(ctx, w, 100) + cancel() + select { + case <-w.RunDone(): + case <-time.After(10 * time.Second): + t.Fatal("timeout waiting for wallet to finish running") + } + restartOpts := append(opts, dcrwtest.WithRestartWallet(w)) + w = dcrwtest.RunForTest(testctx.New(t), t, t.params, restartOpts...) + time.Sleep(2 * time.Second) // FIXME: remove after :2317 is fixed. + t.assertCumulativeBalance(w, sendAmount) +} + +// TestRestore tests several wallet restore scenarios. +func TestRestore(t *testing.T) { + if testing.Short() { + t.Skip("Skipping due to -short") + } + + // Setup simnet chain. + params := chaincfg.SimNetParams() + h, err := dcrdtest.New(t, params, nil, nil) + if err != nil { + t.Fatalf("unable to create main harness: %v", err) + } + err = h.SetUp(testctx.New(t), false, 0) + if err != nil { + t.Fatal(err) + } + defer h.TearDownInTest(t) + if _, err := dcrdtest.AdjustedSimnetMiner(testctx.New(t), h.Node, 110); err != nil { + t.Fatal(err) + } + + // Setup voting wallet. + vw, err := dcrdtest.NewVotingWallet(testctx.WithBackground(t), h) + if err != nil { + t.Fatalf("unable to create voting wallet: %v", err) + } + vw.SetMiner(dcrdtest.AdjustedSimnetMinerForClient(h.Node)) + vw.Start(testctx.WithBackground(t)) + + tests := []restoreScenario{ + { + name: "legacy account", + walletAddrPath: "44'/115'/1'/0/5", + wantBalance: true, + }, { + name: "SLIP0044 account", + syncOpt: "spv", + walletAddrPath: "44'/1'/1'/0/5", + wantBalance: true, + }} + + for _, rs := range tests { + for _, syncOpt := range []string{"spv", "rpc"} { + for _, createOpt := range []string{"stdio", "grpc"} { + tc := rs + tc.syncOpt = syncOpt + tc.createOpt = createOpt + name := fmt.Sprintf("%s/%s/%s", + syncOpt, createOpt, tc.name) + t.Run(name, func(t *testing.T) { + // setTestLogger(t) + th := &testHarness{TB: t, h: h, vw: vw, params: params} + testRestore(th, &tc) + }) + } + } + } +} diff --git a/internal/integration/util_test.go b/internal/integration/util_test.go new file mode 100644 index 000000000..8ff21437c --- /dev/null +++ b/internal/integration/util_test.go @@ -0,0 +1,89 @@ +package integration + +import ( + "bytes" + "encoding/hex" + "fmt" + "testing" + "time" + + "decred.org/dcrwallet/v4/rpc/client/dcrwallet" + "github.com/decred/dcrd/chaincfg/chainhash" + "github.com/decred/dcrd/peer/v3" + "github.com/decred/dcrtest/dcrwtest" + "github.com/decred/slog" + "matheusd.com/testctx" +) + +func init() { + // NOTE: This MUST be updated when the main go.mod file is bumped. + dcrwtest.SetDcrwalletMainPkg("decred.org/dcrwallet/v4") +} + +// loggerWriter is an slog backend that writes to a test output. +type loggerWriter struct { + l testing.TB +} + +func (lw loggerWriter) Write(b []byte) (int, error) { + bt := bytes.TrimRight(b, "\r\n") + lw.l.Logf(string(bt)) + return len(b), nil + +} + +// setTestLogger sets the logger to log into the test. Cannot be used in +// parallel tests. +func setTestLogger(t testing.TB) { + // Add logging to ease debugging this test. + lw := loggerWriter{l: t} + bknd := slog.NewBackend(lw) + logger := bknd.Logger("TEST") + logger.SetLevel(slog.LevelTrace) + dcrwtest.UseLogger(logger) + peer.UseLogger(logger) + t.Cleanup(func() { + dcrwtest.UseLogger(slog.Disabled) + }) +} + +func decodeHashes(strs []string) ([]*chainhash.Hash, error) { + hashes := make([]*chainhash.Hash, len(strs)) + for i, s := range strs { + if len(s) != 2*chainhash.HashSize { + return nil, fmt.Errorf("decode hex error") + } + hashes[i] = new(chainhash.Hash) + _, err := hex.Decode(hashes[i][:], []byte(s)) + if err != nil { + return nil, fmt.Errorf("decode hex error") + } + // unreverse hash string bytes + for j := 0; j < 16; j++ { + hashes[i][j], hashes[i][31-j] = hashes[i][31-j], hashes[i][j] + } + } + return hashes, nil +} + +func encodeHashes(hashes []chainhash.Hash) []string { + res := make([]string, len(hashes)) + for i := range res { + res[i] = hashes[i].String() + } + return res +} + +func jrpcClient(t testing.TB, w *dcrwtest.Wallet) *dcrwallet.Client { + t.Helper() + ctx := testctx.WithTimeout(t, 30*time.Second) + jctl := w.JsonRPCCtl() + c, err := jctl.C(ctx) + if err != nil { + t.Fatal("timeout waiting for JSON-RPC client") + + } + + // Convert to the current client type. + return dcrwallet.NewClient(c, w.ChainParams()) +}