diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 34095509f6e7..544fcb1b4b24 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,7 +34,7 @@ jobs: uses: rtCamp/action-slack-notify@v2.2.0 env: SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} - SLACK_CHANNEL: cosmos-sdk + SLACK_CHANNEL: cosmos-tech SLACK_USERNAME: Cosmos SDK Release Bot SLACK_ICON: https://avatars.githubusercontent.com/t/5997665?size=64 SLACK_COLOR: good diff --git a/CHANGELOG.md b/CHANGELOG.md index 44bac07703bc..b1e868110e83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,7 +39,14 @@ Ref: https://keepachangelog.com/en/1.0.0/ ## Unreleased -* nothing +### Improvements + +* [#505](https://github.com/provenance-io/cosmos-sdk/pull/505) Revert [#444](https://github.com/provenance-io/cosmos-sdk/pull/505): Revert [#13881](https://github.com/cosmos/cosmos-sdk/pull/13881) "Optimize iteration on nested cached KV stores and other operations in general". +* [#505](https://github.com/provenance-io/cosmos-sdk/pull/505) Bring in Cosmos-SDK [v0.46.9](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.46.9) changes. + +### Features + +* [#510](https://github.com/provenance-io/cosmos-sdk/pull/510) Add Sanction Tx commands. --- @@ -275,7 +282,34 @@ It also contains the Provenance Blockchain customizations that were part of [v0. # Cosmos-SDK releases -## [v0.46.8](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.46.8) - 2022-01-23 +## [v0.46.10](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.46.10) - 2022-02-16 + +### Improvements + +* (cli) [#14953](https://github.com/cosmos/cosmos-sdk/pull/14953) Enable profiling block replay during abci handshake with `--cpu-profile`. + +## [v0.46.9](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.46.9) - 2022-02-07 + +### Improvements + +* (deps) [#14846](https://github.com/cosmos/cosmos-sdk/pull/14846) Bump btcd. +* (deps) Bump Tendermint version to [v0.34.26](https://github.com/informalsystems/tendermint/releases/tag/v0.34.26). +* (store) [#14189](https://github.com/cosmos/cosmos-sdk/pull/14189) Add config `iavl-lazy-loading` to enable lazy loading of iavl store, to improve start up time of archive nodes, add method `SetLazyLoading` to `CommitMultiStore` interface. + * A new field has been added to the app.toml. This alllows nodes with larger databases to startup quicker + + ```toml + # IAVLLazyLoading enable/disable the lazy loading of iavl store. + # Default is false. + iavl-lazy-loading = "" + ``` + +### Bug Fixes + +* (cli) [#14919](https://github.com/cosmos/cosmos-sdk/pull/#14919) Fix never assigned error when write validators. +* (store) [#14798](https://github.com/cosmos/cosmos-sdk/pull/14798) Copy btree to avoid the problem of modify while iteration. +* (cli) [#14799](https://github.com/cosmos/cosmos-sdk/pull/14799) Fix Evidence CLI query flag parsing (backport #13458) + +## [v0.46.8](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.46.8) - 2023-01-23 ### Improvements diff --git a/baseapp/abci.go b/baseapp/abci.go index e8f096d59766..905846f81e9c 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -425,6 +425,10 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { telemetry.IncrCounter(1, "query", req.Path) defer telemetry.MeasureSince(time.Now(), req.Path) + if req.Path == "/cosmos.tx.v1beta1.Service/BroadcastTx" { + return sdkerrors.QueryResult(sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "can't route a broadcast tx message"), app.trace) + } + // handle gRPC routes first rather than calling splitPath because '/' characters // are used as part of gRPC paths if grpcHandler := app.grpcQueryRouter.Route(req.Path); grpcHandler != nil { diff --git a/baseapp/options.go b/baseapp/options.go index 7dee4f54b88d..195bd74720ae 100644 --- a/baseapp/options.go +++ b/baseapp/options.go @@ -71,6 +71,11 @@ func SetIAVLDisableFastNode(disable bool) func(*BaseApp) { return func(bapp *BaseApp) { bapp.cms.SetIAVLDisableFastNode(disable) } } +// SetIAVLLazyLoading enables/disables lazy loading of the IAVL store. +func SetIAVLLazyLoading(lazyLoading bool) func(*BaseApp) { + return func(bapp *BaseApp) { bapp.cms.SetLazyLoading(lazyLoading) } +} + // SetInterBlockCache provides a BaseApp option function that sets the // inter-block cache. func SetInterBlockCache(cache sdk.MultiStorePersistentCache) func(*BaseApp) { diff --git a/client/flags/flags.go b/client/flags/flags.go index 6dacc237374a..922b7a40ebdc 100644 --- a/client/flags/flags.go +++ b/client/flags/flags.go @@ -81,6 +81,7 @@ const ( FlagReverse = "reverse" FlagTip = "tip" FlagAux = "aux" + FlagAuthority = "authority" // Tendermint logging flags FlagLogLevel = "log_level" diff --git a/crypto/hd/hdpath.go b/crypto/hd/hdpath.go index 172e3e0f76bd..3216bf40e2c6 100644 --- a/crypto/hd/hdpath.go +++ b/crypto/hd/hdpath.go @@ -10,7 +10,7 @@ import ( "strconv" "strings" - "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/btcec/v2" ) // NewParams creates a BIP 44 parameter object from the params: @@ -225,7 +225,7 @@ func derivePrivateKey(privKeyBytes [32]byte, chainCode [32]byte, index uint32, h data = append([]byte{byte(0)}, privKeyBytes[:]...) } else { // this can't return an error: - _, ecPub := btcec.PrivKeyFromBytes(btcec.S256(), privKeyBytes[:]) + _, ecPub := btcec.PrivKeyFromBytes(privKeyBytes[:]) pubkeyBytes := ecPub.SerializeCompressed() data = pubkeyBytes diff --git a/crypto/keys/secp256k1/secp256k1.go b/crypto/keys/secp256k1/secp256k1.go index 51034275cd94..0d5d849d438b 100644 --- a/crypto/keys/secp256k1/secp256k1.go +++ b/crypto/keys/secp256k1/secp256k1.go @@ -8,7 +8,7 @@ import ( "io" "math/big" - secp256k1 "github.com/btcsuite/btcd/btcec" + secp256k1 "github.com/btcsuite/btcd/btcec/v2" "github.com/tendermint/tendermint/crypto" "golang.org/x/crypto/ripemd160" // nolint: staticcheck // necessary for Bitcoin address format @@ -37,7 +37,7 @@ func (privKey *PrivKey) Bytes() []byte { // PubKey performs the point-scalar multiplication from the privKey on the // generator point to get the pubkey. func (privKey *PrivKey) PubKey() cryptotypes.PubKey { - _, pubkeyObject := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey.Key) + _, pubkeyObject := secp256k1.PrivKeyFromBytes(privKey.Key) pk := pubkeyObject.SerializeCompressed() return &PubKey{Key: pk} } diff --git a/crypto/keys/secp256k1/secp256k1_internal_test.go b/crypto/keys/secp256k1/secp256k1_internal_test.go index 7cbe5949f70c..8350f3faa930 100644 --- a/crypto/keys/secp256k1/secp256k1_internal_test.go +++ b/crypto/keys/secp256k1/secp256k1_internal_test.go @@ -5,7 +5,7 @@ import ( "math/big" "testing" - btcSecp256k1 "github.com/btcsuite/btcd/btcec" + btcSecp256k1 "github.com/btcsuite/btcd/btcec/v2" "github.com/stretchr/testify/require" ) diff --git a/crypto/keys/secp256k1/secp256k1_nocgo.go b/crypto/keys/secp256k1/secp256k1_nocgo.go index e12a410a81cc..ac7f521488c0 100644 --- a/crypto/keys/secp256k1/secp256k1_nocgo.go +++ b/crypto/keys/secp256k1/secp256k1_nocgo.go @@ -4,29 +4,22 @@ package secp256k1 import ( - "math/big" - - secp256k1 "github.com/btcsuite/btcd/btcec" + secp256k1 "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/ecdsa" "github.com/tendermint/tendermint/crypto" ) -// used to reject malleable signatures -// see: -// - https://github.com/ethereum/go-ethereum/blob/f9401ae011ddf7f8d2d95020b7446c17f8d98dc1/crypto/signature_nocgo.go#L90-L93 -// - https://github.com/ethereum/go-ethereum/blob/f9401ae011ddf7f8d2d95020b7446c17f8d98dc1/crypto/crypto.go#L39 -var secp256k1halfN = new(big.Int).Rsh(secp256k1.S256().N, 1) - // Sign creates an ECDSA signature on curve Secp256k1, using SHA256 on the msg. // The returned signature will be of the form R || S (in lower-S form). func (privKey *PrivKey) Sign(msg []byte) ([]byte, error) { - priv, _ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey.Key) - sig, err := priv.Sign(crypto.Sha256(msg)) + priv, _ := secp256k1.PrivKeyFromBytes(privKey.Key) + sig, err := ecdsa.SignCompact(priv, crypto.Sha256(msg), false) if err != nil { return nil, err } - sigBytes := serializeSig(sig) - return sigBytes, nil + // remove the first byte which is compactSigRecoveryCode + return sig[1:], nil } // VerifyBytes verifies a signature of the form R || S. @@ -35,7 +28,7 @@ func (pubKey *PubKey) VerifySignature(msg []byte, sigStr []byte) bool { if len(sigStr) != 64 { return false } - pub, err := secp256k1.ParsePubKey(pubKey.Key, secp256k1.S256()) + pub, err := secp256k1.ParsePubKey(pubKey.Key) if err != nil { return false } @@ -43,7 +36,13 @@ func (pubKey *PubKey) VerifySignature(msg []byte, sigStr []byte) bool { signature := signatureFromBytes(sigStr) // Reject malleable signatures. libsecp256k1 does this check but btcec doesn't. // see: https://github.com/ethereum/go-ethereum/blob/f9401ae011ddf7f8d2d95020b7446c17f8d98dc1/crypto/signature_nocgo.go#L90-L93 - if signature.S.Cmp(secp256k1halfN) > 0 { + // Serialize() would negate S value if it is over half order. + // Hence, if the signature is different after Serialize() if should be rejected. + modifiedSignature, parseErr := ecdsa.ParseDERSignature(signature.Serialize()) + if parseErr != nil { + return false + } + if !signature.IsEqual(modifiedSignature) { return false } return signature.Verify(crypto.Sha256(msg), pub) @@ -51,21 +50,10 @@ func (pubKey *PubKey) VerifySignature(msg []byte, sigStr []byte) bool { // Read Signature struct from R || S. Caller needs to ensure // that len(sigStr) == 64. -func signatureFromBytes(sigStr []byte) *secp256k1.Signature { - return &secp256k1.Signature{ - R: new(big.Int).SetBytes(sigStr[:32]), - S: new(big.Int).SetBytes(sigStr[32:64]), - } -} - -// Serialize signature to R || S. -// R, S are padded to 32 bytes respectively. -func serializeSig(sig *secp256k1.Signature) []byte { - rBytes := sig.R.Bytes() - sBytes := sig.S.Bytes() - sigBytes := make([]byte, 64) - // 0 pad the byte arrays from the left if they aren't big enough. - copy(sigBytes[32-len(rBytes):32], rBytes) - copy(sigBytes[64-len(sBytes):64], sBytes) - return sigBytes +func signatureFromBytes(sigStr []byte) *ecdsa.Signature { + var r secp256k1.ModNScalar + r.SetByteSlice(sigStr[:32]) + var s secp256k1.ModNScalar + s.SetByteSlice(sigStr[32:64]) + return ecdsa.NewSignature(&r, &s) } diff --git a/crypto/keys/secp256k1/secp256k1_nocgo_test.go b/crypto/keys/secp256k1/secp256k1_nocgo_test.go index 060b2815a01e..f38a5bf4fd45 100644 --- a/crypto/keys/secp256k1/secp256k1_nocgo_test.go +++ b/crypto/keys/secp256k1/secp256k1_nocgo_test.go @@ -6,7 +6,7 @@ package secp256k1 import ( "testing" - secp256k1 "github.com/btcsuite/btcd/btcec" + secp256k1 "github.com/btcsuite/btcd/btcec/v2" "github.com/stretchr/testify/require" ) @@ -19,20 +19,29 @@ func TestSignatureVerificationAndRejectUpperS(t *testing.T) { priv := GenPrivKey() sigStr, err := priv.Sign(msg) require.NoError(t, err) - sig := signatureFromBytes(sigStr) - require.False(t, sig.S.Cmp(secp256k1halfN) > 0) + var r secp256k1.ModNScalar + r.SetByteSlice(sigStr[:32]) + var s secp256k1.ModNScalar + s.SetByteSlice(sigStr[32:64]) + require.False(t, s.IsOverHalfOrder()) pub := priv.PubKey() require.True(t, pub.VerifySignature(msg, sigStr)) // malleate: - sig.S.Sub(secp256k1.S256().CurveParams.N, sig.S) - require.True(t, sig.S.Cmp(secp256k1halfN) > 0) - malSigStr := serializeSig(sig) + var S256 secp256k1.ModNScalar + S256.SetByteSlice(secp256k1.S256().N.Bytes()) + s.Negate().Add(&S256) + require.True(t, s.IsOverHalfOrder()) + rBytes := r.Bytes() + sBytes := s.Bytes() + malSigStr := make([]byte, 64) + copy(malSigStr[32-len(rBytes):32], rBytes[:]) + copy(malSigStr[64-len(sBytes):64], sBytes[:]) require.False(t, pub.VerifySignature(msg, malSigStr), "VerifyBytes incorrect with malleated & invalid S. sig=%v, key=%v", - sig, + malSigStr, priv, ) } diff --git a/crypto/keys/secp256k1/secp256k1_test.go b/crypto/keys/secp256k1/secp256k1_test.go index 63f5579459f1..651665ad4e8e 100644 --- a/crypto/keys/secp256k1/secp256k1_test.go +++ b/crypto/keys/secp256k1/secp256k1_test.go @@ -7,7 +7,8 @@ import ( "math/big" "testing" - btcSecp256k1 "github.com/btcsuite/btcd/btcec" + btcSecp256k1 "github.com/btcsuite/btcd/btcec/v2" + btcecdsa "github.com/btcsuite/btcd/btcec/v2/ecdsa" "github.com/cosmos/btcutil/base58" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -64,7 +65,7 @@ func TestSignAndValidateSecp256k1(t *testing.T) { // ---- // Test cross packages verification msgHash := crypto.Sha256(msg) - btcPrivKey, btcPubKey := btcSecp256k1.PrivKeyFromBytes(btcSecp256k1.S256(), privKey.Key) + btcPrivKey, btcPubKey := btcSecp256k1.PrivKeyFromBytes(privKey.Key) // This fails: malformed signature: no header magic // btcSig, err := secp256k1.ParseSignature(sig, secp256k1.S256()) // require.NoError(t, err) @@ -77,9 +78,11 @@ func TestSignAndValidateSecp256k1(t *testing.T) { ok := ecdsa.Verify(btcPubKey.ToECDSA(), msgHash, r, s) require.True(t, ok) - sig2, err := btcPrivKey.Sign(msgHash) + sig2, err := btcecdsa.SignCompact(btcPrivKey, msgHash, false) + // Chop off compactSigRecoveryCode. + sig2 = sig2[1:] require.NoError(t, err) - pubKey.VerifySignature(msg, sig2.Serialize()) + pubKey.VerifySignature(msg, sig2) // ---- // Mutate the signature, just one bit. @@ -98,7 +101,7 @@ func TestSecp256k1LoadPrivkeyAndSerializeIsIdentity(t *testing.T) { // This function creates a private and public key in the underlying libraries format. // The private key is basically calling new(big.Int).SetBytes(pk), which removes leading zero bytes - priv, _ := btcSecp256k1.PrivKeyFromBytes(btcSecp256k1.S256(), privKeyBytes[:]) + priv, _ := btcSecp256k1.PrivKeyFromBytes(privKeyBytes[:]) // this takes the bytes returned by `(big int).Bytes()`, and if the length is less than 32 bytes, // pads the bytes from the left with zero bytes. Therefore these two functions composed // result in the identity function on privKeyBytes, hence the following equality check diff --git a/crypto/keys/utils.go b/crypto/keys/utils.go deleted file mode 100644 index 2b81337d33c8..000000000000 --- a/crypto/keys/utils.go +++ /dev/null @@ -1,13 +0,0 @@ -package keys - -import ( - "math/big" - - "github.com/cosmos/cosmos-sdk/crypto/keys/internal/ecdsa" -) - -// Replicates https://github.com/cosmos/cosmos-sdk/blob/44fbb0df9cea049d588e76bf930177d777552cf3/crypto/ledger/ledger_secp256k1.go#L228 -// DO NOT USE. This is a temporary workaround that is cleaned-up in v0.47+ -func IsOverHalfOrder(sigS *big.Int) bool { - return !ecdsa.IsSNormalized(sigS) -} diff --git a/crypto/ledger/ledger_mock.go b/crypto/ledger/ledger_mock.go index 97c6f65f43bb..21e18cc6c99a 100644 --- a/crypto/ledger/ledger_mock.go +++ b/crypto/ledger/ledger_mock.go @@ -4,11 +4,11 @@ package ledger import ( + "errors" "fmt" - "github.com/btcsuite/btcd/btcec" - "github.com/pkg/errors" - + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/ecdsa" "github.com/cosmos/go-bip39" "github.com/tendermint/tendermint/crypto" @@ -56,7 +56,7 @@ func (mock LedgerSECP256K1Mock) GetPublicKeySECP256K1(derivationPath []uint32) ( return nil, err } - _, pubkeyObject := btcec.PrivKeyFromBytes(btcec.S256(), derivedPriv) + _, pubkeyObject := btcec.PrivKeyFromBytes(derivedPriv) return pubkeyObject.SerializeUncompressed(), nil } @@ -70,7 +70,7 @@ func (mock LedgerSECP256K1Mock) GetAddressPubKeySECP256K1(derivationPath []uint3 } // re-serialize in the 33-byte compressed format - cmp, err := btcec.ParsePubKey(pk, btcec.S256()) + cmp, err := btcec.ParsePubKey(pk) if err != nil { return nil, "", fmt.Errorf("error parsing public key: %v", err) } @@ -97,11 +97,8 @@ func (mock LedgerSECP256K1Mock) SignSECP256K1(derivationPath []uint32, message [ return nil, err } - priv, _ := btcec.PrivKeyFromBytes(btcec.S256(), derivedPriv) - sig, err := priv.Sign(crypto.Sha256(message)) - if err != nil { - return nil, err - } + priv, _ := btcec.PrivKeyFromBytes(derivedPriv) + sig := ecdsa.Sign(priv, crypto.Sha256(message)) return sig.Serialize(), nil } diff --git a/crypto/ledger/ledger_notavail.go b/crypto/ledger/ledger_notavail.go index 578c33d4369c..4cc53e211c3f 100644 --- a/crypto/ledger/ledger_notavail.go +++ b/crypto/ledger/ledger_notavail.go @@ -6,7 +6,7 @@ package ledger import ( - "github.com/pkg/errors" + "errors" ) // If ledger support (build tag) has been enabled, which implies a CGO dependency, diff --git a/crypto/ledger/ledger_secp256k1.go b/crypto/ledger/ledger_secp256k1.go index fc862c2d08fc..29f50ad4e212 100644 --- a/crypto/ledger/ledger_secp256k1.go +++ b/crypto/ledger/ledger_secp256k1.go @@ -1,15 +1,15 @@ package ledger import ( + "errors" "fmt" "math/big" "os" - "github.com/btcsuite/btcd/btcec" - "github.com/pkg/errors" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/ecdsa" "github.com/cosmos/cosmos-sdk/crypto/hd" - "github.com/cosmos/cosmos-sdk/crypto/keys" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" "github.com/cosmos/cosmos-sdk/crypto/types" ) @@ -169,24 +169,29 @@ func warnIfErrors(f func() error) { } func convertDERtoBER(signatureDER []byte) ([]byte, error) { - sigDER, err := btcec.ParseDERSignature(signatureDER, btcec.S256()) + sigDER, err := ecdsa.ParseDERSignature(signatureDER) if err != nil { return nil, err } + sigStr := sigDER.Serialize() + // The format of a DER encoded signature is as follows: + // 0x30 0x02 0x02 + r, s := new(big.Int), new(big.Int) + r.SetBytes(sigStr[4 : 4+sigStr[3]]) + s.SetBytes(sigStr[4+sigStr[3]+2:]) + + sModNScalar := new(btcec.ModNScalar) + sModNScalar.SetByteSlice(s.Bytes()) // based on https://github.com/tendermint/btcd/blob/ec996c5/btcec/signature.go#L33-L50 - // low 'S' malleability breaker - sigS := sigDER.S - if keys.IsOverHalfOrder(sigS) { - sigS = new(big.Int).Sub(btcec.S256().N, sigS) + if sModNScalar.IsOverHalfOrder() { + s = new(big.Int).Sub(btcec.S256().N, s) } - rBytes := sigDER.R.Bytes() - sBytes := sigS.Bytes() sigBytes := make([]byte, 64) // 0 pad the byte arrays from the left if they aren't big enough. - copy(sigBytes[32-len(rBytes):32], rBytes) - copy(sigBytes[64-len(sBytes):64], sBytes) + copy(sigBytes[32-len(r.Bytes()):32], r.Bytes()) + copy(sigBytes[64-len(s.Bytes()):64], s.Bytes()) return sigBytes, nil } @@ -198,7 +203,7 @@ func getDevice() (SECP256K1, error) { device, err := discoverLedger() if err != nil { - return nil, errors.Wrap(err, "ledger nano S") + return nil, fmt.Errorf("ledger nano S: %w", err) } return device, nil @@ -252,7 +257,7 @@ func getPubKeyUnsafe(device SECP256K1, path hd.BIP44Params) (types.PubKey, error } // re-serialize in the 33-byte compressed format - cmp, err := btcec.ParsePubKey(publicKey, btcec.S256()) + cmp, err := btcec.ParsePubKey(publicKey) if err != nil { return nil, fmt.Errorf("error parsing public key: %v", err) } @@ -276,7 +281,7 @@ func getPubKeyAddrSafe(device SECP256K1, path hd.BIP44Params, hrp string) (types } // re-serialize in the 33-byte compressed format - cmp, err := btcec.ParsePubKey(publicKey, btcec.S256()) + cmp, err := btcec.ParsePubKey(publicKey) if err != nil { return nil, "", fmt.Errorf("error parsing public key: %v", err) } diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index b21c74c5a28e..57edfc9cc14c 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -26,7 +26,7 @@ module.exports = { label: "sdk", algolia: { id: "QLS2QSP47E", - key: "067b84458bfa80c295e1d4f12c461911", + key: "4d9feeb481e3cfef8f91bbc63e090042", index: "cosmos_network_vue" }, versions: [ diff --git a/go.mod b/go.mod index 4dd640d25f95..d64735289fe5 100644 --- a/go.mod +++ b/go.mod @@ -6,9 +6,9 @@ require ( cosmossdk.io/errors v1.0.0-beta.7 cosmossdk.io/math v1.0.0-beta.3 github.com/99designs/keyring v1.2.1 - github.com/armon/go-metrics v0.4.0 - github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 - github.com/btcsuite/btcd v0.22.2 + github.com/armon/go-metrics v0.4.1 + github.com/bgentry/speakeasy v0.1.0 + github.com/btcsuite/btcd/btcec/v2 v2.3.2 github.com/celestiaorg/smt v0.3.0 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e github.com/cockroachdb/apd/v2 v2.0.2 @@ -18,7 +18,7 @@ require ( github.com/cosmos/cosmos-proto v1.0.0-beta.1 github.com/cosmos/cosmos-sdk/db v1.0.0-beta.1 github.com/cosmos/go-bip39 v1.0.0 - github.com/cosmos/iavl v0.19.4 + github.com/cosmos/iavl v0.19.5 github.com/cosmos/ledger-cosmos-go v0.12.2 github.com/gogo/gateway v1.1.0 github.com/gogo/protobuf v1.3.2 @@ -40,20 +40,21 @@ require ( github.com/manifoldco/promptui v0.9.0 github.com/mattn/go-isatty v0.0.16 github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.12.2 - github.com/prometheus/common v0.34.0 + github.com/prometheus/client_golang v1.14.0 + github.com/prometheus/common v0.37.0 github.com/rakyll/statik v0.1.7 github.com/regen-network/cosmos-proto v0.3.1 github.com/rs/zerolog v1.27.0 github.com/spf13/cast v1.5.0 - github.com/spf13/cobra v1.6.0 + github.com/spf13/cobra v1.6.1 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.13.0 github.com/stretchr/testify v1.8.1 github.com/tendermint/go-amino v0.16.0 - github.com/tendermint/tendermint v0.34.24 + github.com/tendermint/tendermint v0.34.26 github.com/tendermint/tm-db v0.6.7 - golang.org/x/crypto v0.4.0 + github.com/tidwall/btree v1.5.0 + golang.org/x/crypto v0.5.0 golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 google.golang.org/grpc v1.51.0 @@ -69,18 +70,18 @@ require ( cloud.google.com/go/iam v0.7.0 // indirect cloud.google.com/go/storage v1.27.0 // indirect filippo.io/edwards25519 v1.0.0-rc.1 // indirect + github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d // indirect github.com/Workiva/go-datastructures v1.0.53 // indirect github.com/aws/aws-sdk-go v1.40.45 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect - github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect - github.com/cenkalti/backoff/v4 v4.1.1 // indirect + github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cosmos/gorocksdb v1.2.0 // indirect github.com/creachadair/taskgroup v0.3.2 // indirect - github.com/danieljoos/wincred v1.0.2 // indirect + github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect @@ -88,7 +89,7 @@ require ( github.com/dgraph-io/ristretto v0.1.0 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect github.com/dustin/go-humanize v1.0.0 // indirect - github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b // indirect + github.com/dvsekhvalnov/jose2go v1.5.0 // indirect github.com/fatih/color v1.13.0 // indirect github.com/felixge/httpsnoop v1.0.1 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect @@ -117,7 +118,6 @@ require ( github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect - github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d // indirect github.com/klauspost/compress v1.15.11 // indirect github.com/lib/pq v1.10.6 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect @@ -134,7 +134,7 @@ require ( github.com/pelletier/go-toml/v2 v2.0.5 // indirect github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rs/cors v1.8.2 // indirect @@ -148,11 +148,11 @@ require ( github.com/zondax/ledger-go v0.14.1 // indirect go.etcd.io/bbolt v1.3.6 // indirect go.opencensus.io v0.23.0 // indirect - golang.org/x/net v0.4.0 // indirect + golang.org/x/net v0.5.0 // indirect golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect - golang.org/x/sys v0.3.0 // indirect - golang.org/x/term v0.3.0 // indirect - golang.org/x/text v0.5.0 // indirect + golang.org/x/sys v0.4.0 // indirect + golang.org/x/term v0.4.0 // indirect + golang.org/x/text v0.6.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/api v0.102.0 // indirect google.golang.org/appengine v1.6.7 // indirect @@ -163,7 +163,11 @@ require ( ) replace ( - github.com/99designs/keyring => github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76 + // use cosmos fork of keyring + github.com/99designs/keyring => github.com/cosmos/keyring v1.2.0 + // dgrijalva/jwt-go is deprecated and doesn't receive security updates. + // TODO: remove it: https://github.com/cosmos/cosmos-sdk/issues/13134 + github.com/dgrijalva/jwt-go => github.com/golang-jwt/jwt/v4 v4.4.2 // Fix upstream GHSA-h395-qcrw-5vmq vulnerability. // TODO Remove it: https://github.com/cosmos/cosmos-sdk/issues/10409 github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.7.0 @@ -171,5 +175,5 @@ replace ( github.com/jhump/protoreflect => github.com/jhump/protoreflect v1.9.0 // use informal system fork of tendermint - github.com/tendermint/tendermint => github.com/informalsystems/tendermint v0.34.25 + github.com/tendermint/tendermint => github.com/informalsystems/tendermint v0.34.26 ) diff --git a/go.sum b/go.sum index f252578513b4..34e604a5cc93 100644 --- a/go.sum +++ b/go.sum @@ -59,6 +59,8 @@ filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmG filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= git.sr.ht/~sircmpwn/getopt v0.0.0-20191230200459-23622cc906b3/go.mod h1:wMEGFFFNuPos7vHmWXfszqImLppbc0wEhh6JBfJIUgw= git.sr.ht/~sircmpwn/go-bare v0.0.0-20210406120253-ab86bc2846d9/go.mod h1:BVJwbDfVjCjoFiKrhkei6NdGcZYpkDkdyCdg1ukytRA= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1/go.mod h1:fBF9PQNqB8scdgpZ3ufzaLntG0AG7C1WjPMsiFOmfHM= github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3/go.mod h1:KLF4gFr6DcKFZwSuH8w8yEK6DpFl3LP5rhdvAb7Yz5I= github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0/go.mod h1:tPaiy8S5bQ+S5sOiDlINkp7+Ef339+Nz5L5XO+cnOHo= @@ -102,8 +104,8 @@ github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.4.0 h1:yCQqn7dwca4ITXb+CbubHmedzaQYHhNhrEXLYUeEe8Q= -github.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= +github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= @@ -127,17 +129,15 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= +github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2gVpmOtVTJZNodLdLQLn/KsJqFvXwnd/s= -github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/btcsuite/btcd v0.0.0-20190315201642-aa6e0f35703c/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.21.0-beta.0.20201114000516-e9c7a5ac6401/go.mod h1:Sv4JPQ3/M+teHz9Bo5jBpkNcP0x6r7rdihlNL/7tTAs= +github.com/btcsuite/btcd v0.22.1 h1:CnwP9LM/M9xuRrGSCGeMVs9iv09uMqwsVX7EeIpgV2c= github.com/btcsuite/btcd v0.22.1/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y= -github.com/btcsuite/btcd v0.22.2 h1:vBZ+lGGd1XubpOWO67ITJpAEsICWhA0YzqkcpkgNBfo= -github.com/btcsuite/btcd v0.22.2/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y= github.com/btcsuite/btcd/btcec/v2 v2.1.2/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= @@ -164,8 +164,9 @@ github.com/celestiaorg/smt v0.3.0 h1:Hc6m8fIVRajrg/Saf8ivX4xw551LHzOs8kqeadd6h9s github.com/celestiaorg/smt v0.3.0/go.mod h1:/sdYDakowo/XaxS2Fl7CBqtuf/O2uTqF2zmAUFAtAiw= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= +github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= @@ -219,10 +220,10 @@ github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= github.com/cosmos/gorocksdb v1.2.0 h1:d0l3jJG8M4hBouIZq0mDUHZ+zjOx044J3nGRskwTb4Y= github.com/cosmos/gorocksdb v1.2.0/go.mod h1:aaKvKItm514hKfNJpUJXnnOWeBnk2GL4+Qw9NHizILw= -github.com/cosmos/iavl v0.19.4 h1:t82sN+Y0WeqxDLJRSpNd8YFX5URIrT+p8n6oJbJ2Dok= -github.com/cosmos/iavl v0.19.4/go.mod h1:X9PKD3J0iFxdmgNLa7b2LYWdsGd90ToV5cAONApkEPw= -github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76 h1:DdzS1m6o/pCqeZ8VOAit/gyATedRgjvkVI+UCrLpyuU= -github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76/go.mod h1:0mkLWIoZuQ7uBoospo5Q9zIpqq6rYCPJDSUdeCJvPM8= +github.com/cosmos/iavl v0.19.5 h1:rGA3hOrgNxgRM5wYcSCxgQBap7fW82WZgY78V9po/iY= +github.com/cosmos/iavl v0.19.5/go.mod h1:X9PKD3J0iFxdmgNLa7b2LYWdsGd90ToV5cAONApkEPw= +github.com/cosmos/keyring v1.2.0 h1:8C1lBP9xhImmIabyXW4c3vFjjLiBdGCmfLUfeZlV1Yo= +github.com/cosmos/keyring v1.2.0/go.mod h1:fc+wB5KTk9wQ9sDx0kFXB3A0MaeGHM9AwRStKOQ5vOA= github.com/cosmos/ledger-cosmos-go v0.12.2 h1:/XYaBlE2BJxtvpkHiBm97gFGSGmYGKunKyF3nNqAXZA= github.com/cosmos/ledger-cosmos-go v0.12.2/go.mod h1:ZcqYgnfNJ6lAXe4HPtWgarNEY+B74i+2/8MhZw4ziiI= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= @@ -233,8 +234,8 @@ github.com/creachadair/taskgroup v0.3.2/go.mod h1:wieWwecHVzsidg2CsUnFinW1faVN4+ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= -github.com/danieljoos/wincred v1.0.2 h1:zf4bhty2iLuwgjgpraD2E9UbvO+fe54XXGJbOwe23fU= -github.com/danieljoos/wincred v1.0.2/go.mod h1:SnuYRW9lp1oJrZX/dXJqr0cPK5gYXqx3EJbmjhLdK9U= +github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= +github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= 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= @@ -256,7 +257,6 @@ github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KP github.com/dgraph-io/ristretto v0.0.3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI= github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= @@ -273,8 +273,8 @@ github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b h1:HBah4D48ypg3J7Np4N+HY/ZR76fx3HEUGxDU6Uk39oQ= -github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM= +github.com/dvsekhvalnov/jose2go v1.5.0 h1:3j8ya4Z4kMCwT5nXIKFSV84YS+HdqSSO0VsTQxaLAeM= +github.com/dvsekhvalnov/jose2go v1.5.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= @@ -362,6 +362,7 @@ github.com/gogo/gateway v1.1.0 h1:u0SuhL9+Il+UbjM9VIE3ntfRujKbvVpFvNB4HbjeVQ0= github.com/gogo/gateway v1.1.0/go.mod h1:S7rR8FRQyG3QFESeSv4l2WnsyzlCLG0CzBbUUo/mbic= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -561,8 +562,8 @@ github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19y github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE= github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0= github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po= -github.com/informalsystems/tendermint v0.34.25 h1:KlTF1ECfJI2KmM1w1YGai5hoLJ0ZOKjVwGAaElEBPtE= -github.com/informalsystems/tendermint v0.34.25/go.mod h1:TCGT4eRe5OW979YKVTpFOM57B4YkN+7FSDWpsgzAGwY= +github.com/informalsystems/tendermint v0.34.26 h1:89XvVexAy62geGWxmDmdmmJvfindx+Su2oTuwfSWMeU= +github.com/informalsystems/tendermint v0.34.26/go.mod h1:q3uAZ/t5+MblQhFuHSd4flqaLDx7iUtWpwWbwvHAFhs= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -597,8 +598,6 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8 github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0= github.com/karalabe/usb v0.0.2/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= -github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d h1:Z+RDyXzjKE0i2sTjZ/b1uxiGtPhFy34Ou/Tk0qwN0kM= -github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d/go.mod h1:JJNrCn9otv/2QP4D7SMJBgaleKpOf66PnW6F5WGNRIc= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= @@ -719,6 +718,7 @@ github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxzi github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/neilotoole/errgroup v0.1.6/go.mod h1:Q2nLGf+594h0CLBs/Mbg6qOr7GtqDK7C2S41udRnToE= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nishanths/predeclared v0.0.0-20200524104333-86fad755b4d3/go.mod h1:nt3d53pc1VYcphSCIaYAJtnPYnr3Zyn8fMq2wvPGPso= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= @@ -790,15 +790,16 @@ github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3O github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34= -github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= +github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= @@ -809,8 +810,8 @@ github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB8 github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.34.0 h1:RBmGO9d/FVjqHT0yUGQwBJhkwKV+wPCn7KGpvfab0uE= -github.com/prometheus/common v0.34.0/go.mod h1:gB3sOl7P0TvJabZpLY5uQMpUqRCPPCyRLCZYc7JZTNE= +github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= +github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -877,8 +878,8 @@ github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.6.0 h1:42a0n6jwCot1pUmomAp4T7DeMD+20LFv4Q54pxLf2LI= -github.com/spf13/cobra v1.6.0/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= +github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= +github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= @@ -895,7 +896,6 @@ github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3 github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -918,6 +918,8 @@ github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2l github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= github.com/tendermint/tm-db v0.6.7 h1:fE00Cbl0jayAoqlExN6oyQJ7fR/ZtoVOmvPJ//+shu8= github.com/tendermint/tm-db v0.6.7/go.mod h1:byQDzFkZV1syXr/ReXS808NxA2xvyuuVgXOJ/088L6I= +github.com/tidwall/btree v1.5.0 h1:iV0yVY/frd7r6qGBXfEYs7DH0gTDgrKTrDjS7xt/IyQ= +github.com/tidwall/btree v1.5.0/go.mod h1:LGm8L/DZjPLmeWGjv5kFrY8dL4uVhMmzmmLYmsObdKE= github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.14.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= @@ -1006,8 +1008,8 @@ golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8= -golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= +golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= +golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1105,8 +1107,8 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= -golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1151,7 +1153,6 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1207,6 +1208,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1214,13 +1216,13 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 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.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg= +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1230,8 +1232,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1435,6 +1437,7 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks 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-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/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/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= diff --git a/server/config/config.go b/server/config/config.go index d08b38ded660..79148522d723 100644 --- a/server/config/config.go +++ b/server/config/config.go @@ -91,6 +91,9 @@ type BaseConfig struct { // IAVLDisableFastNode enables or disables the fast sync node. IAVLDisableFastNode bool `mapstructure:"iavl-disable-fastnode"` + // IAVLLazyLoading enable/disable the lazy loading of iavl store. + IAVLLazyLoading bool `mapstructure:"iavl-lazy-loading"` + // AppDBBackend defines the type of Database to use for the application and snapshots databases. // An empty string indicates that the Tendermint config's DBBackend value should be used. AppDBBackend string `mapstructure:"app-db-backend"` @@ -267,6 +270,7 @@ func DefaultConfig() *Config { IndexEvents: make([]string, 0), IAVLCacheSize: 781250, // 50 MB IAVLDisableFastNode: true, + IAVLLazyLoading: false, AppDBBackend: "", }, Telemetry: telemetry.Config{ diff --git a/server/config/toml.go b/server/config/toml.go index 67cfaf78cf0c..5215593f458c 100644 --- a/server/config/toml.go +++ b/server/config/toml.go @@ -78,6 +78,10 @@ iavl-cache-size = {{ .BaseConfig.IAVLCacheSize }} # Default is false. iavl-disable-fastnode = {{ .BaseConfig.IAVLDisableFastNode }} +# EXPERIMENTAL: IAVLLazyLoading enable/disable the lazy loading of iavl store. +# Default is false. +iavl-lazy-loading = {{ .BaseConfig.IAVLLazyLoading }} + # AppDBBackend defines the database backend type to use for the application and snapshots DBs. # An empty string indicates that a fallback will be used. # First fallback is the deprecated compile-time types.DBBackend value. diff --git a/server/mock/store.go b/server/mock/store.go index 2c0c2a0c26f3..d3de9715991e 100644 --- a/server/mock/store.go +++ b/server/mock/store.go @@ -138,6 +138,10 @@ func (ms multiStore) SetIAVLDisableFastNode(disable bool) { panic("not implemented") } +func (ms multiStore) SetLazyLoading(bool) { + panic("not implemented") +} + func (ms multiStore) SetInitialVersion(version int64) error { panic("not implemented") } diff --git a/server/rosetta/converter.go b/server/rosetta/converter.go index 7e0e27376a9d..e7cce56bd134 100644 --- a/server/rosetta/converter.go +++ b/server/rosetta/converter.go @@ -7,7 +7,7 @@ import ( "reflect" "cosmossdk.io/math" - "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/btcec/v2" rosettatypes "github.com/coinbase/rosetta-sdk-go/types" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto" @@ -649,7 +649,7 @@ func (c converter) PubKey(pubKey *rosettatypes.PublicKey) (cryptotypes.PubKey, e return nil, crgerrs.WrapError(crgerrs.ErrUnsupportedCurve, "only secp256k1 supported") } - cmp, err := btcec.ParsePubKey(pubKey.Bytes, btcec.S256()) + cmp, err := btcec.ParsePubKey(pubKey.Bytes) if err != nil { return nil, crgerrs.WrapError(crgerrs.ErrBadArgument, err.Error()) } diff --git a/server/start.go b/server/start.go index ea366ee05870..181c171b33c5 100644 --- a/server/start.go +++ b/server/start.go @@ -58,6 +58,7 @@ const ( FlagMinRetainBlocks = "min-retain-blocks" FlagIAVLCacheSize = "iavl-cache-size" FlagDisableIAVLFastNode = "iavl-disable-fastnode" + FlagIAVLLazyLoading = "iavl-lazy-loading" // state sync-related flags FlagStateSyncSnapshotInterval = "state-sync.snapshot-interval" @@ -136,11 +137,15 @@ is performed. Note, when enabled, gRPC will also be automatically enabled. withTM, _ := cmd.Flags().GetBool(flagWithTendermint) if !withTM { serverCtx.Logger.Info("starting ABCI without Tendermint") - return startStandAlone(serverCtx, appCreator) + return wrapCPUProfile(serverCtx, func() error { + return startStandAlone(serverCtx, appCreator) + }) } // amino is needed here for backwards compatibility of REST routes - err = startInProcess(serverCtx, clientCtx, appCreator) + err = wrapCPUProfile(serverCtx, func() error { + return startInProcess(serverCtx, clientCtx, appCreator) + }) errCode, ok := err.(ErrorCode) if !ok { return err @@ -248,27 +253,6 @@ func startStandAlone(ctx *Context, appCreator types.AppCreator) error { func startInProcess(ctx *Context, clientCtx client.Context, appCreator types.AppCreator) error { cfg := ctx.Config home := cfg.RootDir - var cpuProfileCleanup func() - - if cpuProfile := ctx.Viper.GetString(flagCPUProfile); cpuProfile != "" { - f, err := os.Create(cpuProfile) - if err != nil { - return err - } - - ctx.Logger.Info("starting CPU profiler", "profile", cpuProfile) - if err := pprof.StartCPUProfile(f); err != nil { - return err - } - - cpuProfileCleanup = func() { - ctx.Logger.Info("stopping CPU profiler", "profile", cpuProfile) - pprof.StopCPUProfile() - if err := f.Close(); err != nil { - ctx.Logger.Info("failed to close cpu-profile file", "profile", cpuProfile, "err", err.Error()) - } - } - } db, err := openDB(home, GetAppDBBackend(ctx.Viper)) if err != nil { @@ -503,10 +487,6 @@ func startInProcess(ctx *Context, clientCtx client.Context, appCreator types.App _ = tmNode.Stop() } - if cpuProfileCleanup != nil { - cpuProfileCleanup() - } - if apiSrv != nil { _ = apiSrv.Close() } @@ -524,3 +504,40 @@ func startTelemetry(cfg serverconfig.Config) (*telemetry.Metrics, error) { } return telemetry.New(cfg.Telemetry) } + +// wrapCPUProfile runs callback in a goroutine, then wait for quit signals. +func wrapCPUProfile(ctx *Context, callback func() error) error { + if cpuProfile := ctx.Viper.GetString(flagCPUProfile); cpuProfile != "" { + f, err := os.Create(cpuProfile) + if err != nil { + return err + } + + ctx.Logger.Info("starting CPU profiler", "profile", cpuProfile) + if err := pprof.StartCPUProfile(f); err != nil { + return err + } + + defer func() { + ctx.Logger.Info("stopping CPU profiler", "profile", cpuProfile) + pprof.StopCPUProfile() + if err := f.Close(); err != nil { + ctx.Logger.Info("failed to close cpu-profile file", "profile", cpuProfile, "err", err.Error()) + } + }() + } + + errCh := make(chan error) + go func() { + errCh <- callback() + }() + + select { + case err := <-errCh: + return err + + case <-time.After(types.ServerStartTime): + } + + return WaitForQuitSignals() +} diff --git a/server/util.go b/server/util.go index e211bd245844..1834da6c3384 100644 --- a/server/util.go +++ b/server/util.go @@ -457,5 +457,6 @@ func DefaultBaseappOptions(appOpts types.AppOptions) []func(*baseapp.BaseApp) { baseapp.SetSnapshot(snapshotStore, snapshotOptions), baseapp.SetIAVLCacheSize(cast.ToInt(appOpts.Get(FlagIAVLCacheSize))), baseapp.SetIAVLDisableFastNode(cast.ToBool(appOpts.Get(FlagDisableIAVLFastNode))), + baseapp.SetIAVLLazyLoading(cast.ToBool(appOpts.Get(FlagIAVLLazyLoading))), } } diff --git a/store/cachekv/benchmark_test.go b/store/cachekv/benchmark_test.go new file mode 100644 index 000000000000..2db62ba5d6c6 --- /dev/null +++ b/store/cachekv/benchmark_test.go @@ -0,0 +1,161 @@ +package cachekv_test + +import ( + fmt "fmt" + "testing" + + "github.com/cosmos/cosmos-sdk/store" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/tendermint/tendermint/libs/log" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + dbm "github.com/tendermint/tm-db" +) + +func DoBenchmarkDeepContextStack(b *testing.B, depth int) { + begin := []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + end := []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + key := storetypes.NewKVStoreKey("test") + + db := dbm.NewMemDB() + cms := store.NewCommitMultiStore(db) + cms.MountStoreWithDB(key, storetypes.StoreTypeIAVL, db) + cms.LoadLatestVersion() + ctx := sdk.NewContext(cms, tmproto.Header{}, false, log.NewNopLogger()) + + var stack ContextStack + stack.Reset(ctx) + + for i := 0; i < depth; i++ { + stack.Snapshot() + + store := stack.CurrentContext().KVStore(key) + store.Set(begin, []byte("value")) + } + + store := stack.CurrentContext().KVStore(key) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + it := store.Iterator(begin, end) + it.Valid() + it.Key() + it.Value() + it.Next() + it.Close() + } +} + +func BenchmarkDeepContextStack1(b *testing.B) { + DoBenchmarkDeepContextStack(b, 1) +} + +func BenchmarkDeepContextStack3(b *testing.B) { + DoBenchmarkDeepContextStack(b, 3) +} +func BenchmarkDeepContextStack10(b *testing.B) { + DoBenchmarkDeepContextStack(b, 10) +} + +func BenchmarkDeepContextStack13(b *testing.B) { + DoBenchmarkDeepContextStack(b, 13) +} + +// cachedContext is a pair of cache context and its corresponding commit method. +// They are obtained from the return value of `context.CacheContext()`. +type cachedContext struct { + ctx sdk.Context + commit func() +} + +// ContextStack manages the initial context and a stack of cached contexts, +// to support the `StateDB.Snapshot` and `StateDB.RevertToSnapshot` methods. +// +// Copied from an old version of ethermint +type ContextStack struct { + // Context of the initial state before transaction execution. + // It's the context used by `StateDB.CommitedState`. + initialCtx sdk.Context + cachedContexts []cachedContext +} + +// CurrentContext returns the top context of cached stack, +// if the stack is empty, returns the initial context. +func (cs *ContextStack) CurrentContext() sdk.Context { + l := len(cs.cachedContexts) + if l == 0 { + return cs.initialCtx + } + return cs.cachedContexts[l-1].ctx +} + +// Reset sets the initial context and clear the cache context stack. +func (cs *ContextStack) Reset(ctx sdk.Context) { + cs.initialCtx = ctx + if len(cs.cachedContexts) > 0 { + cs.cachedContexts = []cachedContext{} + } +} + +// IsEmpty returns true if the cache context stack is empty. +func (cs *ContextStack) IsEmpty() bool { + return len(cs.cachedContexts) == 0 +} + +// Commit commits all the cached contexts from top to bottom in order and clears the stack by setting an empty slice of cache contexts. +func (cs *ContextStack) Commit() { + // commit in order from top to bottom + for i := len(cs.cachedContexts) - 1; i >= 0; i-- { + if cs.cachedContexts[i].commit == nil { + panic(fmt.Sprintf("commit function at index %d should not be nil", i)) + } else { + cs.cachedContexts[i].commit() + } + } + cs.cachedContexts = []cachedContext{} +} + +// CommitToRevision commit the cache after the target revision, +// to improve efficiency of db operations. +func (cs *ContextStack) CommitToRevision(target int) error { + if target < 0 || target >= len(cs.cachedContexts) { + return fmt.Errorf("snapshot index %d out of bound [%d..%d)", target, 0, len(cs.cachedContexts)) + } + + // commit in order from top to bottom + for i := len(cs.cachedContexts) - 1; i > target; i-- { + if cs.cachedContexts[i].commit == nil { + return fmt.Errorf("commit function at index %d should not be nil", i) + } + cs.cachedContexts[i].commit() + } + cs.cachedContexts = cs.cachedContexts[0 : target+1] + + return nil +} + +// Snapshot pushes a new cached context to the stack, +// and returns the index of it. +func (cs *ContextStack) Snapshot() int { + i := len(cs.cachedContexts) + ctx, commit := cs.CurrentContext().CacheContext() + cs.cachedContexts = append(cs.cachedContexts, cachedContext{ctx: ctx, commit: commit}) + return i +} + +// RevertToSnapshot pops all the cached contexts after the target index (inclusive). +// the target should be snapshot index returned by `Snapshot`. +// This function panics if the index is out of bounds. +func (cs *ContextStack) RevertToSnapshot(target int) { + if target < 0 || target >= len(cs.cachedContexts) { + panic(fmt.Errorf("snapshot index %d out of bound [%d..%d)", target, 0, len(cs.cachedContexts))) + } + cs.cachedContexts = cs.cachedContexts[:target] +} + +// RevertAll discards all the cache contexts. +func (cs *ContextStack) RevertAll() { + if len(cs.cachedContexts) > 0 { + cs.RevertToSnapshot(0) + } +} diff --git a/store/cachekv/internal/btree.go b/store/cachekv/internal/btree.go new file mode 100644 index 000000000000..8d18caa1853d --- /dev/null +++ b/store/cachekv/internal/btree.go @@ -0,0 +1,86 @@ +package internal + +import ( + "bytes" + "errors" + + "github.com/tidwall/btree" +) + +const ( + // The approximate number of items and children per B-tree node. Tuned with benchmarks. + // copied from memdb. + bTreeDegree = 32 +) + +var errKeyEmpty = errors.New("key cannot be empty") + +// BTree implements the sorted cache for cachekv store, +// we don't use MemDB here because cachekv is used extensively in sdk core path, +// we need it to be as fast as possible, while `MemDB` is mainly used as a mocking db in unit tests. +// +// We choose tidwall/btree over google/btree here because it provides API to implement step iterator directly. +type BTree struct { + tree btree.BTreeG[item] +} + +// NewBTree creates a wrapper around `btree.BTreeG`. +func NewBTree() *BTree { + return &BTree{tree: *btree.NewBTreeGOptions(byKeys, btree.Options{ + Degree: bTreeDegree, + // Contract: cachekv store must not be called concurrently + NoLocks: true, + })} +} + +func (bt *BTree) Set(key, value []byte) { + bt.tree.Set(newItem(key, value)) +} + +func (bt *BTree) Get(key []byte) []byte { + i, found := bt.tree.Get(newItem(key, nil)) + if !found { + return nil + } + return i.value +} + +func (bt *BTree) Delete(key []byte) { + bt.tree.Delete(newItem(key, nil)) +} + +func (bt *BTree) Iterator(start, end []byte) (*memIterator, error) { + if (start != nil && len(start) == 0) || (end != nil && len(end) == 0) { + return nil, errKeyEmpty + } + return NewMemIterator(start, end, bt, make(map[string]struct{}), true), nil +} + +func (bt *BTree) ReverseIterator(start, end []byte) (*memIterator, error) { + if (start != nil && len(start) == 0) || (end != nil && len(end) == 0) { + return nil, errKeyEmpty + } + return NewMemIterator(start, end, bt, make(map[string]struct{}), false), nil +} + +func (bt *BTree) Copy() *BTree { + return &BTree{ + tree: *bt.tree.Copy(), + } +} + +// item is a btree item with byte slices as keys and values +type item struct { + key []byte + value []byte +} + +// byKeys compares the items by key +func byKeys(a, b item) bool { + return bytes.Compare(a.key, b.key) == -1 +} + +// newItem creates a new pair item. +func newItem(key, value []byte) item { + return item{key: key, value: value} +} diff --git a/store/cachekv/internal/btree_test.go b/store/cachekv/internal/btree_test.go new file mode 100644 index 000000000000..f85a8bbaf109 --- /dev/null +++ b/store/cachekv/internal/btree_test.go @@ -0,0 +1,202 @@ +package internal + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" +) + +func TestGetSetDelete(t *testing.T) { + db := NewBTree() + + // A nonexistent key should return nil. + value := db.Get([]byte("a")) + require.Nil(t, value) + + // Set and get a value. + db.Set([]byte("a"), []byte{0x01}) + db.Set([]byte("b"), []byte{0x02}) + value = db.Get([]byte("a")) + require.Equal(t, []byte{0x01}, value) + + value = db.Get([]byte("b")) + require.Equal(t, []byte{0x02}, value) + + // Deleting a non-existent value is fine. + db.Delete([]byte("x")) + + // Delete a value. + db.Delete([]byte("a")) + + value = db.Get([]byte("a")) + require.Nil(t, value) + + db.Delete([]byte("b")) + + value = db.Get([]byte("b")) + require.Nil(t, value) +} + +func TestDBIterator(t *testing.T) { + db := NewBTree() + + for i := 0; i < 10; i++ { + if i != 6 { // but skip 6. + db.Set(int642Bytes(int64(i)), []byte{}) + } + } + + // Blank iterator keys should error + _, err := db.ReverseIterator([]byte{}, nil) + require.Equal(t, errKeyEmpty, err) + _, err = db.ReverseIterator(nil, []byte{}) + require.Equal(t, errKeyEmpty, err) + + itr, err := db.Iterator(nil, nil) + require.NoError(t, err) + verifyIterator(t, itr, []int64{0, 1, 2, 3, 4, 5, 7, 8, 9}, "forward iterator") + + ritr, err := db.ReverseIterator(nil, nil) + require.NoError(t, err) + verifyIterator(t, ritr, []int64{9, 8, 7, 5, 4, 3, 2, 1, 0}, "reverse iterator") + + itr, err = db.Iterator(nil, int642Bytes(0)) + require.NoError(t, err) + verifyIterator(t, itr, []int64(nil), "forward iterator to 0") + + ritr, err = db.ReverseIterator(int642Bytes(10), nil) + require.NoError(t, err) + verifyIterator(t, ritr, []int64(nil), "reverse iterator from 10 (ex)") + + itr, err = db.Iterator(int642Bytes(0), nil) + require.NoError(t, err) + verifyIterator(t, itr, []int64{0, 1, 2, 3, 4, 5, 7, 8, 9}, "forward iterator from 0") + + itr, err = db.Iterator(int642Bytes(1), nil) + require.NoError(t, err) + verifyIterator(t, itr, []int64{1, 2, 3, 4, 5, 7, 8, 9}, "forward iterator from 1") + + ritr, err = db.ReverseIterator(nil, int642Bytes(10)) + require.NoError(t, err) + verifyIterator(t, ritr, + []int64{9, 8, 7, 5, 4, 3, 2, 1, 0}, "reverse iterator from 10 (ex)") + + ritr, err = db.ReverseIterator(nil, int642Bytes(9)) + require.NoError(t, err) + verifyIterator(t, ritr, + []int64{8, 7, 5, 4, 3, 2, 1, 0}, "reverse iterator from 9 (ex)") + + ritr, err = db.ReverseIterator(nil, int642Bytes(8)) + require.NoError(t, err) + verifyIterator(t, ritr, + []int64{7, 5, 4, 3, 2, 1, 0}, "reverse iterator from 8 (ex)") + + itr, err = db.Iterator(int642Bytes(5), int642Bytes(6)) + require.NoError(t, err) + verifyIterator(t, itr, []int64{5}, "forward iterator from 5 to 6") + + itr, err = db.Iterator(int642Bytes(5), int642Bytes(7)) + require.NoError(t, err) + verifyIterator(t, itr, []int64{5}, "forward iterator from 5 to 7") + + itr, err = db.Iterator(int642Bytes(5), int642Bytes(8)) + require.NoError(t, err) + verifyIterator(t, itr, []int64{5, 7}, "forward iterator from 5 to 8") + + itr, err = db.Iterator(int642Bytes(6), int642Bytes(7)) + require.NoError(t, err) + verifyIterator(t, itr, []int64(nil), "forward iterator from 6 to 7") + + itr, err = db.Iterator(int642Bytes(6), int642Bytes(8)) + require.NoError(t, err) + verifyIterator(t, itr, []int64{7}, "forward iterator from 6 to 8") + + itr, err = db.Iterator(int642Bytes(7), int642Bytes(8)) + require.NoError(t, err) + verifyIterator(t, itr, []int64{7}, "forward iterator from 7 to 8") + + ritr, err = db.ReverseIterator(int642Bytes(4), int642Bytes(5)) + require.NoError(t, err) + verifyIterator(t, ritr, []int64{4}, "reverse iterator from 5 (ex) to 4") + + ritr, err = db.ReverseIterator(int642Bytes(4), int642Bytes(6)) + require.NoError(t, err) + verifyIterator(t, ritr, + []int64{5, 4}, "reverse iterator from 6 (ex) to 4") + + ritr, err = db.ReverseIterator(int642Bytes(4), int642Bytes(7)) + require.NoError(t, err) + verifyIterator(t, ritr, + []int64{5, 4}, "reverse iterator from 7 (ex) to 4") + + ritr, err = db.ReverseIterator(int642Bytes(5), int642Bytes(6)) + require.NoError(t, err) + verifyIterator(t, ritr, []int64{5}, "reverse iterator from 6 (ex) to 5") + + ritr, err = db.ReverseIterator(int642Bytes(5), int642Bytes(7)) + require.NoError(t, err) + verifyIterator(t, ritr, []int64{5}, "reverse iterator from 7 (ex) to 5") + + ritr, err = db.ReverseIterator(int642Bytes(6), int642Bytes(7)) + require.NoError(t, err) + verifyIterator(t, ritr, + []int64(nil), "reverse iterator from 7 (ex) to 6") + + ritr, err = db.ReverseIterator(int642Bytes(10), nil) + require.NoError(t, err) + verifyIterator(t, ritr, []int64(nil), "reverse iterator to 10") + + ritr, err = db.ReverseIterator(int642Bytes(6), nil) + require.NoError(t, err) + verifyIterator(t, ritr, []int64{9, 8, 7}, "reverse iterator to 6") + + ritr, err = db.ReverseIterator(int642Bytes(5), nil) + require.NoError(t, err) + verifyIterator(t, ritr, []int64{9, 8, 7, 5}, "reverse iterator to 5") + + ritr, err = db.ReverseIterator(int642Bytes(8), int642Bytes(9)) + require.NoError(t, err) + verifyIterator(t, ritr, []int64{8}, "reverse iterator from 9 (ex) to 8") + + ritr, err = db.ReverseIterator(int642Bytes(2), int642Bytes(4)) + require.NoError(t, err) + verifyIterator(t, ritr, + []int64{3, 2}, "reverse iterator from 4 (ex) to 2") + + ritr, err = db.ReverseIterator(int642Bytes(4), int642Bytes(2)) + require.NoError(t, err) + verifyIterator(t, ritr, + []int64(nil), "reverse iterator from 2 (ex) to 4") + + // Ensure that the iterators don't panic with an empty database. + db2 := NewBTree() + + itr, err = db2.Iterator(nil, nil) + require.NoError(t, err) + verifyIterator(t, itr, nil, "forward iterator with empty db") + + ritr, err = db2.ReverseIterator(nil, nil) + require.NoError(t, err) + verifyIterator(t, ritr, nil, "reverse iterator with empty db") +} + +func verifyIterator(t *testing.T, itr *memIterator, expected []int64, msg string) { + i := 0 + for itr.Valid() { + key := itr.Key() + require.Equal(t, expected[i], bytes2Int64(key), "iterator: %d mismatches", i) + itr.Next() + i++ + } + require.Equal(t, i, len(expected), "expected to have fully iterated over all the elements in iter") + require.NoError(t, itr.Close()) +} + +func int642Bytes(i int64) []byte { + return sdk.Uint64ToBigEndian(uint64(i)) +} + +func bytes2Int64(buf []byte) int64 { + return int64(sdk.BigEndianToUint64(buf)) +} diff --git a/store/cachekv/internal/memiterator.go b/store/cachekv/internal/memiterator.go new file mode 100644 index 000000000000..2bceb8bc77df --- /dev/null +++ b/store/cachekv/internal/memiterator.go @@ -0,0 +1,137 @@ +package internal + +import ( + "bytes" + "errors" + + "github.com/cosmos/cosmos-sdk/store/types" + "github.com/tidwall/btree" +) + +var _ types.Iterator = (*memIterator)(nil) + +// memIterator iterates over iterKVCache items. +// if key is nil, means it was deleted. +// Implements Iterator. +type memIterator struct { + iter btree.GenericIter[item] + + start []byte + end []byte + ascending bool + lastKey []byte + deleted map[string]struct{} + valid bool +} + +func NewMemIterator(start, end []byte, items *BTree, deleted map[string]struct{}, ascending bool) *memIterator { + iter := items.tree.Iter() + var valid bool + if ascending { + if start != nil { + valid = iter.Seek(newItem(start, nil)) + } else { + valid = iter.First() + } + } else { + if end != nil { + valid = iter.Seek(newItem(end, nil)) + if !valid { + valid = iter.Last() + } else { + // end is exclusive + valid = iter.Prev() + } + } else { + valid = iter.Last() + } + } + + mi := &memIterator{ + iter: iter, + start: start, + end: end, + ascending: ascending, + lastKey: nil, + deleted: deleted, + valid: valid, + } + + if mi.valid { + mi.valid = mi.keyInRange(mi.Key()) + } + + return mi +} + +func (mi *memIterator) Domain() (start []byte, end []byte) { + return mi.start, mi.end +} + +func (mi *memIterator) Close() error { + mi.iter.Release() + return nil +} + +func (mi *memIterator) Error() error { + if !mi.Valid() { + return errors.New("invalid memIterator") + } + return nil +} + +func (mi *memIterator) Valid() bool { + return mi.valid +} + +func (mi *memIterator) Next() { + mi.assertValid() + + if mi.ascending { + mi.valid = mi.iter.Next() + } else { + mi.valid = mi.iter.Prev() + } + + if mi.valid { + mi.valid = mi.keyInRange(mi.Key()) + } +} + +func (mi *memIterator) keyInRange(key []byte) bool { + if mi.ascending && mi.end != nil && bytes.Compare(key, mi.end) >= 0 { + return false + } + if !mi.ascending && mi.start != nil && bytes.Compare(key, mi.start) < 0 { + return false + } + return true +} + +func (mi *memIterator) Key() []byte { + return mi.iter.Item().key +} + +func (mi *memIterator) Value() []byte { + item := mi.iter.Item() + key := item.key + // We need to handle the case where deleted is modified and includes our current key + // We handle this by maintaining a lastKey object in the iterator. + // If the current key is the same as the last key (and last key is not nil / the start) + // then we are calling value on the same thing as last time. + // Therefore we don't check the mi.deleted to see if this key is included in there. + if _, ok := mi.deleted[string(key)]; ok { + if mi.lastKey == nil || !bytes.Equal(key, mi.lastKey) { + // not re-calling on old last key + return nil + } + } + mi.lastKey = key + return item.value +} + +func (mi *memIterator) assertValid() { + if err := mi.Error(); err != nil { + panic(err) + } +} diff --git a/store/cachekv/mergeiterator.go b/store/cachekv/internal/mergeiterator.go similarity index 86% rename from store/cachekv/mergeiterator.go rename to store/cachekv/internal/mergeiterator.go index a6c7a035aba0..4186a178a863 100644 --- a/store/cachekv/mergeiterator.go +++ b/store/cachekv/internal/mergeiterator.go @@ -1,4 +1,4 @@ -package cachekv +package internal import ( "bytes" @@ -18,17 +18,20 @@ type cacheMergeIterator struct { parent types.Iterator cache types.Iterator ascending bool + + valid bool } var _ types.Iterator = (*cacheMergeIterator)(nil) -func newCacheMergeIterator(parent, cache types.Iterator, ascending bool) *cacheMergeIterator { +func NewCacheMergeIterator(parent, cache types.Iterator, ascending bool) *cacheMergeIterator { iter := &cacheMergeIterator{ parent: parent, cache: cache, ascending: ascending, } + iter.valid = iter.skipUntilExistsOrInvalid() return iter } @@ -40,42 +43,38 @@ func (iter *cacheMergeIterator) Domain() (start, end []byte) { // Valid implements Iterator. func (iter *cacheMergeIterator) Valid() bool { - return iter.skipUntilExistsOrInvalid() + return iter.valid } // Next implements Iterator func (iter *cacheMergeIterator) Next() { - iter.skipUntilExistsOrInvalid() iter.assertValid() - // If parent is invalid, get the next cache item. - if !iter.parent.Valid() { + switch { + case !iter.parent.Valid(): + // If parent is invalid, get the next cache item. iter.cache.Next() - return - } - - // If cache is invalid, get the next parent item. - if !iter.cache.Valid() { + case !iter.cache.Valid(): + // If cache is invalid, get the next parent item. iter.parent.Next() - return - } - - // Both are valid. Compare keys. - keyP, keyC := iter.parent.Key(), iter.cache.Key() - switch iter.compare(keyP, keyC) { - case -1: // parent < cache - iter.parent.Next() - case 0: // parent == cache - iter.parent.Next() - iter.cache.Next() - case 1: // parent > cache - iter.cache.Next() + default: + // Both are valid. Compare keys. + keyP, keyC := iter.parent.Key(), iter.cache.Key() + switch iter.compare(keyP, keyC) { + case -1: // parent < cache + iter.parent.Next() + case 0: // parent == cache + iter.parent.Next() + iter.cache.Next() + case 1: // parent > cache + iter.cache.Next() + } } + iter.valid = iter.skipUntilExistsOrInvalid() } // Key implements Iterator func (iter *cacheMergeIterator) Key() []byte { - iter.skipUntilExistsOrInvalid() iter.assertValid() // If parent is invalid, get the cache key. @@ -106,7 +105,6 @@ func (iter *cacheMergeIterator) Key() []byte { // Value implements Iterator func (iter *cacheMergeIterator) Value() []byte { - iter.skipUntilExistsOrInvalid() iter.assertValid() // If parent is invalid, get the cache value. @@ -137,11 +135,12 @@ func (iter *cacheMergeIterator) Value() []byte { // Close implements Iterator func (iter *cacheMergeIterator) Close() error { + err1 := iter.cache.Close() if err := iter.parent.Close(); err != nil { return err } - return iter.cache.Close() + return err1 } // Error returns an error if the cacheMergeIterator is invalid defined by the diff --git a/store/cachekv/memiterator.go b/store/cachekv/memiterator.go deleted file mode 100644 index a12ff9acfd11..000000000000 --- a/store/cachekv/memiterator.go +++ /dev/null @@ -1,57 +0,0 @@ -package cachekv - -import ( - "bytes" - - dbm "github.com/tendermint/tm-db" - - "github.com/cosmos/cosmos-sdk/store/types" -) - -// memIterator iterates over iterKVCache items. -// if key is nil, means it was deleted. -// Implements Iterator. -type memIterator struct { - types.Iterator - - lastKey []byte - deleted map[string]struct{} -} - -func newMemIterator(start, end []byte, items *dbm.MemDB, deleted map[string]struct{}, ascending bool) *memIterator { - var ( - iter types.Iterator - err error - ) - - if ascending { - iter, err = items.Iterator(start, end) - } else { - iter, err = items.ReverseIterator(start, end) - } - - if err != nil { - panic(err) - } - - return &memIterator{ - Iterator: iter, - lastKey: nil, - deleted: deleted, - } -} - -func (mi *memIterator) Value() []byte { - key := mi.Iterator.Key() - // We need to handle the case where deleted is modified and includes our current key - // We handle this by maintaining a lastKey object in the iterator. - // If the current key is the same as the last key (and last key is not nil / the start) - // then we are calling value on the same thing as last time. - // Therefore we don't check the mi.deleted to see if this key is included in there. - reCallingOnOldLastKey := (mi.lastKey != nil) && bytes.Equal(key, mi.lastKey) - if _, ok := mi.deleted[string(key)]; ok && !reCallingOnOldLastKey { - return nil - } - mi.lastKey = key - return mi.Iterator.Value() -} diff --git a/store/cachekv/search_benchmark_test.go b/store/cachekv/search_benchmark_test.go index 921bff4e3864..4007c7cda202 100644 --- a/store/cachekv/search_benchmark_test.go +++ b/store/cachekv/search_benchmark_test.go @@ -4,7 +4,7 @@ import ( "strconv" "testing" - db "github.com/tendermint/tm-db" + "github.com/cosmos/cosmos-sdk/store/cachekv/internal" ) func BenchmarkLargeUnsortedMisses(b *testing.B) { @@ -39,6 +39,6 @@ func generateStore() *Store { return &Store{ cache: cache, unsortedCache: unsorted, - sortedCache: db.NewMemDB(), + sortedCache: internal.NewBTree(), } } diff --git a/store/cachekv/store.go b/store/cachekv/store.go index 0ebc52268548..36be56eea751 100644 --- a/store/cachekv/store.go +++ b/store/cachekv/store.go @@ -9,10 +9,10 @@ import ( dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/internal/conv" + "github.com/cosmos/cosmos-sdk/store/cachekv/internal" "github.com/cosmos/cosmos-sdk/store/tracekv" "github.com/cosmos/cosmos-sdk/store/types" "github.com/cosmos/cosmos-sdk/types/kv" - "github.com/tendermint/tendermint/libs/math" ) // cValue represents a cached value. @@ -30,7 +30,7 @@ type Store struct { cache map[string]*cValue deleted map[string]struct{} unsortedCache map[string]struct{} - sortedCache *dbm.MemDB // always ascending sorted + sortedCache *internal.BTree // always ascending sorted parent types.KVStore } @@ -42,7 +42,7 @@ func NewStore(parent types.KVStore) *Store { cache: make(map[string]*cValue), deleted: make(map[string]struct{}), unsortedCache: make(map[string]struct{}), - sortedCache: dbm.NewMemDB(), + sortedCache: internal.NewBTree(), parent: parent, } } @@ -101,6 +101,11 @@ func (store *Store) Write() { store.mtx.Lock() defer store.mtx.Unlock() + if len(store.cache) == 0 && len(store.deleted) == 0 && len(store.unsortedCache) == 0 { + store.sortedCache = internal.NewBTree() + return + } + // We need a copy of all of the keys. // Not the best, but probably not a bottleneck depending. keys := make([]string, 0, len(store.cache)) @@ -144,7 +149,7 @@ func (store *Store) Write() { for key := range store.unsortedCache { delete(store.unsortedCache, key) } - store.sortedCache = dbm.NewMemDB() + store.sortedCache = internal.NewBTree() } // CacheWrap implements CacheWrapper. @@ -183,9 +188,9 @@ func (store *Store) iterator(start, end []byte, ascending bool) types.Iterator { } store.dirtyItems(start, end) - cache = newMemIterator(start, end, store.sortedCache, store.deleted, ascending) + cache = internal.NewMemIterator(start, end, store.sortedCache.Copy(), store.deleted, ascending) - return newCacheMergeIterator(parent, cache, ascending) + return internal.NewCacheMergeIterator(parent, cache, ascending) } func findStartIndex(strL []string, startQ string) int { @@ -273,7 +278,7 @@ const minSortSize = 1024 // Constructs a slice of dirty items, to use w/ memIterator. func (store *Store) dirtyItems(start, end []byte) { startStr, endStr := conv.UnsafeBytesToStr(start), conv.UnsafeBytesToStr(end) - if startStr > endStr { + if end != nil && startStr > endStr { // Nothing to do here. return } @@ -288,6 +293,7 @@ func (store *Store) dirtyItems(start, end []byte) { // than just not having the cache. if n < minSortSize { for key := range store.unsortedCache { + // dbm.IsKeyInDomain is nil safe and returns true iff key is greater than start if dbm.IsKeyInDomain(conv.UnsafeStrToBytes(key), start, end) { cacheValue := store.cache[key] unsorted = append(unsorted, &kv.Pair{Key: []byte(key), Value: cacheValue.value}) @@ -308,24 +314,18 @@ func (store *Store) dirtyItems(start, end []byte) { // Now find the values within the domain // [start, end) startIndex := findStartIndex(strL, startStr) - endIndex := findEndIndex(strL, endStr) - - if endIndex < 0 { - endIndex = len(strL) - 1 - } if startIndex < 0 { startIndex = 0 } - // Since we spent cycles to sort the values, we should process and remove a reasonable amount - // ensure start to end is at least minSortSize in size - // if below minSortSize, expand it to cover additional values - // this amortizes the cost of processing elements across multiple calls - if endIndex-startIndex < minSortSize { - endIndex = math.MinInt(startIndex+minSortSize, len(strL)-1) - if endIndex-startIndex < minSortSize { - startIndex = math.MaxInt(endIndex-minSortSize, 0) - } + var endIndex int + if end == nil { + endIndex = len(strL) - 1 + } else { + endIndex = findEndIndex(strL, endStr) + } + if endIndex < 0 { + endIndex = len(strL) - 1 } kvL := make([]*kv.Pair, 0) @@ -364,10 +364,7 @@ func (store *Store) clearUnsortedCacheSubset(unsorted []*kv.Pair, sortState sort store.sortedCache.Set(item.Key, []byte{}) continue } - err := store.sortedCache.Set(item.Key, item.Value) - if err != nil { - panic(err) - } + store.sortedCache.Set(item.Key, item.Value) } } diff --git a/store/cachekv/store_test.go b/store/cachekv/store_test.go index d589932d30fc..2bf59f59d5c3 100644 --- a/store/cachekv/store_test.go +++ b/store/cachekv/store_test.go @@ -2,6 +2,7 @@ package cachekv_test import ( "fmt" + "strconv" "testing" "github.com/stretchr/testify/require" @@ -120,6 +121,7 @@ func TestCacheKVIteratorBounds(t *testing.T) { i++ } require.Equal(t, nItems, i) + require.NoError(t, itr.Close()) // iterate over none itr = st.Iterator(bz("money"), nil) @@ -128,6 +130,7 @@ func TestCacheKVIteratorBounds(t *testing.T) { i++ } require.Equal(t, 0, i) + require.NoError(t, itr.Close()) // iterate over lower itr = st.Iterator(keyFmt(0), keyFmt(3)) @@ -139,6 +142,7 @@ func TestCacheKVIteratorBounds(t *testing.T) { i++ } require.Equal(t, 3, i) + require.NoError(t, itr.Close()) // iterate over upper itr = st.Iterator(keyFmt(2), keyFmt(4)) @@ -150,6 +154,64 @@ func TestCacheKVIteratorBounds(t *testing.T) { i++ } require.Equal(t, 4, i) + require.NoError(t, itr.Close()) +} + +func TestCacheKVReverseIteratorBounds(t *testing.T) { + st := newCacheKVStore() + + // set some items + nItems := 5 + for i := 0; i < nItems; i++ { + st.Set(keyFmt(i), valFmt(i)) + } + + // iterate over all of them + itr := st.ReverseIterator(nil, nil) + i := 0 + for ; itr.Valid(); itr.Next() { + k, v := itr.Key(), itr.Value() + require.Equal(t, keyFmt(nItems-1-i), k) + require.Equal(t, valFmt(nItems-1-i), v) + i++ + } + require.Equal(t, nItems, i) + require.NoError(t, itr.Close()) + + // iterate over none + itr = st.ReverseIterator(bz("money"), nil) + i = 0 + for ; itr.Valid(); itr.Next() { + i++ + } + require.Equal(t, 0, i) + require.NoError(t, itr.Close()) + + // iterate over lower + end := 3 + itr = st.ReverseIterator(keyFmt(0), keyFmt(end)) + i = 0 + for ; itr.Valid(); itr.Next() { + i++ + k, v := itr.Key(), itr.Value() + require.Equal(t, keyFmt(end-i), k) + require.Equal(t, valFmt(end-i), v) + } + require.Equal(t, 3, i) + require.NoError(t, itr.Close()) + + // iterate over upper + end = 4 + itr = st.ReverseIterator(keyFmt(2), keyFmt(end)) + i = 0 + for ; itr.Valid(); itr.Next() { + i++ + k, v := itr.Key(), itr.Value() + require.Equal(t, keyFmt(end-i), k) + require.Equal(t, valFmt(end-i), v) + } + require.Equal(t, 2, i) + require.NoError(t, itr.Close()) } func TestCacheKVMergeIteratorBasics(t *testing.T) { @@ -291,6 +353,25 @@ func TestCacheKVMergeIteratorChunks(t *testing.T) { assertIterateDomainCheck(t, st, truth, []keyRange{{0, 15}, {25, 35}, {38, 40}, {45, 80}}) } +func TestCacheKVMergeIteratorDomain(t *testing.T) { + st := newCacheKVStore() + + itr := st.Iterator(nil, nil) + start, end := itr.Domain() + require.Equal(t, start, end) + require.NoError(t, itr.Close()) + + itr = st.Iterator(keyFmt(40), keyFmt(60)) + start, end = itr.Domain() + require.Equal(t, keyFmt(40), start) + require.Equal(t, keyFmt(60), end) + require.NoError(t, itr.Close()) + + start, end = st.ReverseIterator(keyFmt(0), keyFmt(80)).Domain() + require.Equal(t, keyFmt(0), start) + require.Equal(t, keyFmt(80), end) +} + func TestCacheKVMergeIteratorRandom(t *testing.T) { st := newCacheKVStore() truth := dbm.NewMemDB() @@ -306,6 +387,67 @@ func TestCacheKVMergeIteratorRandom(t *testing.T) { } } +func TestNilEndIterator(t *testing.T) { + const SIZE = 3000 + + tests := []struct { + name string + write bool + startIndex int + end []byte + }{ + {name: "write=false, end=nil", write: false, end: nil, startIndex: 1000}, + {name: "write=false, end=nil; full key scan", write: false, end: nil, startIndex: 2000}, + {name: "write=true, end=nil", write: true, end: nil, startIndex: 1000}, + {name: "write=false, end=non-nil", write: false, end: keyFmt(3000), startIndex: 1000}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + st := newCacheKVStore() + + for i := 0; i < SIZE; i++ { + kstr := keyFmt(i) + st.Set(kstr, valFmt(i)) + } + + if tt.write { + st.Write() + } + + itr := st.Iterator(keyFmt(tt.startIndex), tt.end) + i := tt.startIndex + j := 0 + for itr.Valid() { + require.Equal(t, keyFmt(i), itr.Key()) + require.Equal(t, valFmt(i), itr.Value()) + itr.Next() + i++ + j++ + } + + require.Equal(t, SIZE-tt.startIndex, j) + require.NoError(t, itr.Close()) + }) + } +} + +// TestIteratorDeadlock demonstrate the deadlock issue in cache store. +func TestIteratorDeadlock(t *testing.T) { + mem := dbadapter.Store{DB: dbm.NewMemDB()} + store := cachekv.NewStore(mem) + // the channel buffer is 64 and received once, so put at least 66 elements. + for i := 0; i < 66; i++ { + store.Set([]byte(fmt.Sprintf("key%d", i)), []byte{1}) + } + it := store.Iterator(nil, nil) + defer it.Close() + store.Set([]byte("key20"), []byte{1}) + // it'll be blocked here with previous version, or enable lock on btree. + it2 := store.Iterator(nil, nil) + defer it2.Close() +} + //------------------------------------------------------------------------------------------- // do some random ops @@ -388,6 +530,7 @@ func assertIterateDomain(t *testing.T, st types.KVStore, expectedN int) { i++ } require.Equal(t, expectedN, i) + require.NoError(t, itr.Close()) } func assertIterateDomainCheck(t *testing.T, st types.KVStore, mem dbm.DB, r []keyRange) { @@ -419,6 +562,8 @@ func assertIterateDomainCheck(t *testing.T, st types.KVStore, mem dbm.DB, r []ke require.False(t, itr.Valid()) require.False(t, itr2.Valid()) + require.NoError(t, itr.Close()) + require.NoError(t, itr2.Close()) } func assertIterateDomainCompare(t *testing.T, st types.KVStore, mem dbm.DB) { @@ -428,6 +573,8 @@ func assertIterateDomainCompare(t *testing.T, st types.KVStore, mem dbm.DB) { require.NoError(t, err) checkIterators(t, itr, itr2) checkIterators(t, itr2, itr) + require.NoError(t, itr.Close()) + require.NoError(t, itr2.Close()) } func checkIterators(t *testing.T, itr, itr2 types.Iterator) { @@ -538,3 +685,46 @@ func BenchmarkCacheKVStoreGetKeyFound(b *testing.B) { st.Get([]byte{byte((i & 0xFF0000) >> 16), byte((i & 0xFF00) >> 8), byte(i & 0xFF)}) } } + +func TestIteratorNested(t *testing.T) { + mem := dbadapter.Store{DB: dbm.NewMemDB()} + store := cachekv.NewStore(mem) + + // setup: + // - (owner, contract id) -> contract + // - (contract id, record id) -> record + owner1 := 1 + contract1 := 1 + contract2 := 2 + record1 := 1 + record2 := 2 + store.Set([]byte(fmt.Sprintf("c%04d%04d", owner1, contract1)), []byte("contract1")) + store.Set([]byte(fmt.Sprintf("c%04d%04d", owner1, contract2)), []byte("contract2")) + store.Set([]byte(fmt.Sprintf("R%04d%04d", contract1, record1)), []byte("contract1-record1")) + store.Set([]byte(fmt.Sprintf("R%04d%04d", contract1, record2)), []byte("contract1-record2")) + store.Set([]byte(fmt.Sprintf("R%04d%04d", contract2, record1)), []byte("contract2-record1")) + store.Set([]byte(fmt.Sprintf("R%04d%04d", contract2, record2)), []byte("contract2-record2")) + + it := types.KVStorePrefixIterator(store, []byte(fmt.Sprintf("c%04d", owner1))) + defer it.Close() + + var records []string + for ; it.Valid(); it.Next() { + contractID, err := strconv.ParseInt(string(it.Key()[5:]), 10, 32) + require.NoError(t, err) + + it2 := types.KVStorePrefixIterator(store, []byte(fmt.Sprintf("R%04d", contractID))) + for ; it2.Valid(); it2.Next() { + records = append(records, string(it2.Value())) + } + + it2.Close() + } + + require.Equal(t, []string{ + "contract1-record1", + "contract1-record2", + "contract2-record1", + "contract2-record2", + }, records) +} diff --git a/store/iavl/store.go b/store/iavl/store.go index a22d8935c202..3cf6c1dedbf2 100644 --- a/store/iavl/store.go +++ b/store/iavl/store.go @@ -239,6 +239,11 @@ func (st *Store) LoadVersionForOverwriting(targetVersion int64) (int64, error) { return st.tree.LoadVersionForOverwriting(targetVersion) } +// LazyLoadVersionForOverwriting is the lazy version of LoadVersionForOverwriting. +func (st *Store) LazyLoadVersionForOverwriting(targetVersion int64) (int64, error) { + return st.tree.LazyLoadVersionForOverwriting(targetVersion) +} + // Implements types.KVStore. func (st *Store) Iterator(start, end []byte) types.Iterator { iterator, err := st.tree.Iterator(start, end, true) @@ -273,7 +278,7 @@ func (st *Store) Export(version int64) (*iavl.Exporter, error) { if !ok || tree == nil { return nil, fmt.Errorf("iavl export failed: unable to fetch tree for version %v", version) } - return tree.Export(), nil + return tree.Export() } // Import imports an IAVL tree at the given version, returning an iavl.Importer for importing. diff --git a/store/iavl/tree.go b/store/iavl/tree.go index 81350fc34a8b..f8e2843f61c6 100644 --- a/store/iavl/tree.go +++ b/store/iavl/tree.go @@ -35,6 +35,7 @@ type ( Iterator(start, end []byte, ascending bool) (types.Iterator, error) AvailableVersions() []int LoadVersionForOverwriting(targetVersion int64) (int64, error) + LazyLoadVersionForOverwriting(targetVersion int64) (int64, error) } // immutableTree is a simple wrapper around a reference to an iavl.ImmutableTree @@ -104,3 +105,7 @@ func (it *immutableTree) AvailableVersions() []int { func (it *immutableTree) LoadVersionForOverwriting(targetVersion int64) (int64, error) { panic("cannot call 'LoadVersionForOverwriting' on an immutable IAVL tree") } + +func (it *immutableTree) LazyLoadVersionForOverwriting(targetVersion int64) (int64, error) { + panic("cannot call 'LazyLoadVersionForOverwriting' on an immutable IAVL tree") +} diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go index 1c3b097dd018..52f5c94d6915 100644 --- a/store/rootmulti/store.go +++ b/store/rootmulti/store.go @@ -1006,7 +1006,12 @@ func (rs *Store) RollbackToVersion(target int64) error { // If the store is wrapped with an inter-block cache, we must first unwrap // it to get the underlying IAVL store. store = rs.GetCommitKVStore(key) - _, err := store.(*iavl.Store).LoadVersionForOverwriting(target) + var err error + if rs.lazyLoading { + _, err = store.(*iavl.Store).LazyLoadVersionForOverwriting(target) + } else { + _, err = store.(*iavl.Store).LoadVersionForOverwriting(target) + } if err != nil { return err } diff --git a/store/types/store.go b/store/types/store.go index 5e41854b77ac..33b254bbc750 100644 --- a/store/types/store.go +++ b/store/types/store.go @@ -188,6 +188,9 @@ type CommitMultiStore interface { // SetIAVLDisableFastNode enables/disables fastnode feature on iavl. SetIAVLDisableFastNode(disable bool) + // SetIAVLLazyLoading enable/disable lazy loading on iavl. + SetLazyLoading(lazyLoading bool) + // RollbackToVersion rollback the db to specific version(height). RollbackToVersion(version int64) error diff --git a/x/evidence/client/cli/query.go b/x/evidence/client/cli/query.go index bf74325f949f..e40465a84b9a 100644 --- a/x/evidence/client/cli/query.go +++ b/x/evidence/client/cli/query.go @@ -32,7 +32,6 @@ $ %s query %s --page=2 --limit=50 ), ), Args: cobra.MaximumNArgs(1), - DisableFlagParsing: true, SuggestionsMinimumDistance: 2, RunE: QueryEvidenceCmd(), } diff --git a/x/gov/client/cli/util.go b/x/gov/client/cli/util.go index de44dc5a78dc..830d4871bbfd 100644 --- a/x/gov/client/cli/util.go +++ b/x/gov/client/cli/util.go @@ -9,7 +9,9 @@ import ( "github.com/spf13/pflag" "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" govutils "github.com/cosmos/cosmos-sdk/x/gov/client/utils" govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" @@ -148,3 +150,31 @@ func ReadGovPropFlags(clientCtx client.Context, flagSet *pflag.FlagSet) (*govv1. return rv, nil } + +// GenerateOrBroadcastTxCLIAsGovProp wraps the provided msgs in a governance proposal +// and calls GenerateOrBroadcastTxCLI for that proposal. +// At least one msg is required. +// This uses flags added by AddGovPropFlagsToCmd to fill in the rest of the proposal. +func GenerateOrBroadcastTxCLIAsGovProp(clientCtx client.Context, flagSet *pflag.FlagSet, msgs ...sdk.Msg) error { + if len(msgs) == 0 { + return fmt.Errorf("no messages to submit") + } + + prop, err := ReadGovPropFlags(clientCtx, flagSet) + if err != nil { + return err + } + + prop.Messages = make([]*codectypes.Any, len(msgs)) + for i, msg := range msgs { + prop.Messages[i], err = codectypes.NewAnyWithValue(msg) + if err != nil { + if len(msgs) == 1 { + return fmt.Errorf("could not wrap %T message as Any: %w", msg, err) + } + return fmt.Errorf("could not wrap message %d (%T) as Any: %w", i, msg, err) + } + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, flagSet, prop) +} diff --git a/x/gov/client/cli/util_test.go b/x/gov/client/cli/util_test.go index 21aa72d769cb..4e592025bb20 100644 --- a/x/gov/client/cli/util_test.go +++ b/x/gov/client/cli/util_test.go @@ -2,10 +2,12 @@ package cli import ( "bytes" + "context" "encoding/base64" "fmt" "io" "os" + "runtime/debug" "strings" "testing" @@ -14,6 +16,7 @@ import ( "github.com/stretchr/testify/require" "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/testutil" @@ -25,6 +28,23 @@ import ( stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) +// convertPanicToErrorWithStack runs the provided runner. +// If it neither panics nor returns an error, nil is returned. +// If it returns an error, that error is returned. +// If it panics, an error with the panic message and stack trace is returned. +func convertPanicToErrorWithStack(runner func() error) (err error) { + defer func() { + if r := recover(); r != nil { + if e, ok := r.(error); ok { + err = fmt.Errorf("%w\n%s", e, string(debug.Stack())) + } else { + err = fmt.Errorf("%#v%s", r, string(debug.Stack())) + } + } + }() + return runner() +} + func TestParseSubmitLegacyProposalFlags(t *testing.T) { okJSON := testutil.WriteToNewTempFile(t, ` { @@ -461,3 +481,136 @@ func TestReadGovPropFlags(t *testing.T) { }) } } + +func TestGenerateOrBroadcastTxCLIAsGovProp(t *testing.T) { + fromAddr := sdk.AccAddress("another_from_address") + argDeposit := "--" + FlagDeposit + + tests := []struct { + name string + args []string + msgs []sdk.Msg + expErr []string + }{ + { + name: "control", + args: []string{argDeposit, "30goodcoin"}, + msgs: []sdk.Msg{ + &stakingtypes.MsgDelegate{ + DelegatorAddress: fromAddr.String(), + ValidatorAddress: sdk.ValAddress("1_validator_address_").String(), + Amount: sdk.NewInt64Coin("blargh", 42), + }, + &stakingtypes.MsgDelegate{ + DelegatorAddress: fromAddr.String(), + ValidatorAddress: sdk.ValAddress("2_validator_address_").String(), + Amount: sdk.NewInt64Coin("hgralb", 24), + }, + }, + // I don't care to test what happens in GenerateOrBroadcastTxCLI, + // which is the last thing called in GenerateOrBroadcastTxCLIAsGovProp. + // And setting it up so that GenerateOrBroadcastTxCLI has everything needed + // to not give an error is a major pain. + // But, I can test that execution got to that point by checking for + // a standard thing in the panic/error/stack. + expErr: []string{ + ".GenerateOrBroadcastTxCLI(", + ".GenerateOrBroadcastTxWithFactory(", + ".Factory.Prepare(", + "runtime error: invalid memory address or nil pointer dereference", + }, + }, + { + name: "no messages", + args: []string{argDeposit, "30emptycoin"}, + msgs: nil, + expErr: []string{"no messages to submit"}, + }, + { + name: "read gov prop flags fails", + args: []string{argDeposit, "notcoins"}, + msgs: []sdk.Msg{ + &stakingtypes.MsgDelegate{ + DelegatorAddress: fromAddr.String(), + ValidatorAddress: sdk.ValAddress("3_validator_address_").String(), + Amount: sdk.NewInt64Coin("gogogo", 99), + }, + }, + expErr: []string{"invalid deposit", "invalid decimal coin expression", "notcoins"}, + }, + { + name: "one message nil", + args: []string{argDeposit, "30nilcoin"}, + msgs: []sdk.Msg{nil}, + expErr: []string{"could not wrap message as Any", "Expecting non nil value to create a new Any"}, + }, + { + name: "two messages first nil", + args: []string{argDeposit, "32onecoin"}, + msgs: []sdk.Msg{ + nil, + &stakingtypes.MsgDelegate{ + DelegatorAddress: fromAddr.String(), + ValidatorAddress: sdk.ValAddress("4_validator_address_").String(), + Amount: sdk.NewInt64Coin("foundcoin", 200), + }, + }, + expErr: []string{"could not wrap message 0 () as Any", "Expecting non nil value to create a new Any"}, + }, + { + name: "two messages second nil", + args: []string{argDeposit, "31twocoin"}, + msgs: []sdk.Msg{ + &stakingtypes.MsgDelegate{ + DelegatorAddress: fromAddr.String(), + ValidatorAddress: sdk.ValAddress("5_validator_address_").String(), + Amount: sdk.NewInt64Coin("inccoin", 123), + }, + nil, + }, + expErr: []string{"could not wrap message 1 () as Any", "Expecting non nil value to create a new Any"}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + // Create a dummy command to get stuff from. + cmd := &cobra.Command{ + Short: tc.name, + Run: func(cmd *cobra.Command, args []string) { + t.Errorf("The cmd for %q has run with the args %q, but Run shouldn't have been called.", tc.name, args) + }, + } + AddGovPropFlagsToCmd(cmd) + flags.AddTxFlagsToCmd(cmd) + + // Use it to parse the provided flags and get the resulting flagSet. + err := cmd.ParseFlags(tc.args) + require.NoError(t, err, "parsing test case args using cmd: %q", tc.args) + flagSet := cmd.Flags() + + // Give it a context and then retrieve it. + cmd.SetContext(context.WithValue(context.Background(), client.ClientContextKey, &client.Context{})) + clientCtx, err := client.GetClientTxContext(cmd) + require.NoError(t, err, "GetClientTxContext") + // Set the From Address so that the resulting proposal will have a proposer. + clientCtx.FromAddress = fromAddr + + // Run the function being tested. + testFunc := func() error { + return GenerateOrBroadcastTxCLIAsGovProp(clientCtx, flagSet, tc.msgs...) + } + err = convertPanicToErrorWithStack(testFunc) + + // Make sure the error has what's expected. + if len(tc.expErr) > 0 { + require.Error(t, err, "GenerateOrBroadcastTxCLIAsGovProp error") + for _, exp := range tc.expErr { + assert.ErrorContains(t, err, exp, "GenerateOrBroadcastTxCLIAsGovProp error") + } + } else { + require.NoError(t, err, "GenerateOrBroadcastTxCLIAsGovProp error") + } + }) + } +} diff --git a/x/sanction/client/cli/tx.go b/x/sanction/client/cli/tx.go new file mode 100644 index 000000000000..fda535fb2bdb --- /dev/null +++ b/x/sanction/client/cli/tx.go @@ -0,0 +1,207 @@ +package cli + +import ( + "fmt" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + govcli "github.com/cosmos/cosmos-sdk/x/gov/client/cli" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/cosmos/cosmos-sdk/x/sanction" +) + +var ( + // DefaultAuthorityAddr is the default authority to provide in the sanction module's governance proposal messages. + // It should match the value provided to the sanction keeper constructor. + // It is defined as a sdk.AccAddress to be independent of global bech32 HRP definition. + DefaultAuthorityAddr = authtypes.NewModuleAddress(govtypes.ModuleName) + + // exampleTxCmdBase is the base command that gets a user to one of the tx commands in here. + exampleTxCmdBase = fmt.Sprintf("%s tx %s", version.AppName, sanction.ModuleName) + // exampleTxAddr1 is a constant address for use in example strings. + exampleTxAddr1 = sdk.AccAddress("exampleTxAddr1______") + // exampleTxAddr2 is a constant address for use in example strings. + exampleTxAddr2 = sdk.AccAddress("exampleTxAddr2______") +) + +// TxCmd returns the command with sub-commands for specific sanction module Tx interaction. +func TxCmd() *cobra.Command { + txCmd := &cobra.Command{ + Use: sanction.ModuleName, + Short: "Sanction transaction subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + txCmd.AddCommand( + TxSanctionCmd(), + TxUnsanctionCmd(), + TxUpdateParamsCmd(), + ) + + return txCmd +} + +// TxSanctionCmd returns the command for submitting a MsgSanction governance proposal tx. +func TxSanctionCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "sanction
[
...]", + Short: "Submit a governance proposal to sanction one or more addresses", + Long: `Submit a governance proposal to sanction one or more addresses. +At least one address is required; any number of addresses can be provided. +Each address should be a valid bech32 encoded string.`, + Example: fmt.Sprintf(` +$ %[1]s sanction %[2]s +$ %[1]s sanction %[3]s %[2]s +`, + exampleTxCmdBase, exampleTxAddr1, exampleTxAddr2), + Args: cobra.MinimumNArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + flagSet := cmd.Flags() + + msgSanction := &sanction.MsgSanction{ + Addresses: args, + Authority: getAuthority(flagSet), + } + if err = msgSanction.ValidateBasic(); err != nil { + return err + } + + return govcli.GenerateOrBroadcastTxCLIAsGovProp(clientCtx, flagSet, msgSanction) + }, + } + + flags.AddTxFlagsToCmd(cmd) + govcli.AddGovPropFlagsToCmd(cmd) + addAuthorityFlagToCmd(cmd) + + return cmd +} + +// TxUnsanctionCmd returns the command for submitting a MsgUnsanction governance proposal tx. +func TxUnsanctionCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "unsanction
[
...]", + Short: "Submit a governance proposal to unsanction one or more addresses", + Long: `Submit a governance proposal to unsanction one or more addresses. +At least one address is required; any number of addresses can be provided. +Each address should be a valid bech32 encoded string.`, + Example: fmt.Sprintf(` +$ %[1]s unsanction %[3]s +$ %[1]s unsanction %[2]s %[3]s +`, + exampleTxCmdBase, exampleTxAddr1, exampleTxAddr2), + Args: cobra.MinimumNArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + flagSet := cmd.Flags() + + msgUnsanction := &sanction.MsgUnsanction{ + Addresses: args, + Authority: getAuthority(flagSet), + } + if err = msgUnsanction.ValidateBasic(); err != nil { + return err + } + + return govcli.GenerateOrBroadcastTxCLIAsGovProp(clientCtx, flagSet, msgUnsanction) + }, + } + + flags.AddTxFlagsToCmd(cmd) + govcli.AddGovPropFlagsToCmd(cmd) + addAuthorityFlagToCmd(cmd) + + return cmd +} + +// TxUpdateParamsCmd returns the command for submitting a MsgUpdateParams governance proposal tx. +func TxUpdateParamsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "update-params ", + Short: "Submit a governance proposal to update the sanction module's params", + Long: `Submit a governance proposal to update the sanction module's params. +Both and are required. +They must be coins or empty strings.`, + Example: fmt.Sprintf(` +$ %[1]s update-params 100%[2]s 150%[2]s +$ %[1]s update-params '' 50%[2]s +$ %[1]s update-params 75%[2]s '' +$ %[1]s update-params '' '' +`, + exampleTxCmdBase, sdk.DefaultBondDenom), + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + flagSet := cmd.Flags() + + msgUpdateParams := &sanction.MsgUpdateParams{ + Params: &sanction.Params{}, + Authority: getAuthority(flagSet), + } + + if len(args[0]) > 0 { + msgUpdateParams.Params.ImmediateSanctionMinDeposit, err = sdk.ParseCoinsNormalized(args[0]) + if err != nil { + return fmt.Errorf("invalid immediate_sanction_min_deposit string %q: %w", args[0], err) + } + } + + if len(args[1]) > 0 { + msgUpdateParams.Params.ImmediateUnsanctionMinDeposit, err = sdk.ParseCoinsNormalized(args[1]) + if err != nil { + return fmt.Errorf("invalid immediate_unsanction_min_deposit string %q: %w", args[1], err) + } + } + + if err = msgUpdateParams.ValidateBasic(); err != nil { + return err + } + + return govcli.GenerateOrBroadcastTxCLIAsGovProp(clientCtx, flagSet, msgUpdateParams) + }, + } + + flags.AddTxFlagsToCmd(cmd) + govcli.AddGovPropFlagsToCmd(cmd) + addAuthorityFlagToCmd(cmd) + + return cmd +} + +// addAuthorityFlagToCmd adds the authority flag to a command. +func addAuthorityFlagToCmd(cmd *cobra.Command) { + // Note: Not setting a default here because the HRP might not yet be set correctly. + cmd.Flags().String(flags.FlagAuthority, "", "The authority to use. If not provided, a default is used") +} + +// getAuthority gets the authority string from the flagSet or returns the default. +func getAuthority(flagSet *pflag.FlagSet) string { + // Ignoring the error here since we really don't care, + // and it's easier if this just returns a string. + authority, _ := flagSet.GetString(flags.FlagAuthority) + if len(authority) > 0 { + return authority + } + return DefaultAuthorityAddr.String() +} diff --git a/x/sanction/client/testutil/cli_test.go b/x/sanction/client/testutil/cli_test.go index 83ccd688787d..7a67fdef918e 100644 --- a/x/sanction/client/testutil/cli_test.go +++ b/x/sanction/client/testutil/cli_test.go @@ -21,7 +21,6 @@ import ( "github.com/cosmos/cosmos-sdk/testutil/cli" "github.com/cosmos/cosmos-sdk/testutil/network" sdk "github.com/cosmos/cosmos-sdk/types" - authcli "github.com/cosmos/cosmos-sdk/x/auth/client/cli" govcli "github.com/cosmos/cosmos-sdk/x/gov/client/cli" gov "github.com/cosmos/cosmos-sdk/x/gov/types" govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" @@ -84,7 +83,7 @@ func TestIntegrationTestSuite(t *testing.T) { suite.Run(t, NewIntegrationTestSuite(cfg, &sanctionGen)) } -func (s *IntegrationTestSuite) TestSanctionValidatorImmediate() { +func (s *IntegrationTestSuite) TestSanctionValidatorImmediateUsingGovCmds() { // Wait 2 blocks to start this. That way, hopefully the query tests are done. // In between the two, create all the stuff to send. s.Require().NoError(s.network.WaitForNextBlock(), "wait for next block 1") @@ -285,37 +284,3 @@ func (s *IntegrationTestSuite) logHeight() int64 { s.T().Logf("Current height: %d", height) return height } - -func (s *IntegrationTestSuite) getAuthority() string { - args := []string{"gov", "--" + tmcli.OutputFlag, "json"} - outBW, err := cli.ExecTestCLICmd(s.clientCtx, authcli.QueryModuleAccountByNameCmd(), args) - s.Require().NoError(err, "ExecTestCLICmd q auth module-account gov") - outBz := outBW.Bytes() - s.T().Logf("q auth module-account gov output:\n%s", string(outBz)) - // example output: - // { - // "account": { - // "@type": "/cosmos.auth.v1beta1.ModuleAccount", - // "base_account": { - // "address": "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn", - // "pub_key": null, - // "account_number": "9", - // "sequence": "0" - // }, - // "name": "gov", - // "permissions": ["burner"] - // } - // } - var output map[string]json.RawMessage - err = json.Unmarshal(outBz, &output) - s.Require().NoError(err, "Unmarshal output json") - var account map[string]json.RawMessage - err = json.Unmarshal(output["account"], &account) - s.Require().NoError(err, "Unmarshal account") - var baseAccount map[string]string - err = json.Unmarshal(account["base_account"], &baseAccount) - s.Require().NoError(err, "Unmarshal base_account") - rv := string(baseAccount["address"]) - s.T().Logf("authority: %q", rv) - return rv -} diff --git a/x/sanction/client/testutil/common_test.go b/x/sanction/client/testutil/common_test.go index 125488db3116..564244004eb5 100644 --- a/x/sanction/client/testutil/common_test.go +++ b/x/sanction/client/testutil/common_test.go @@ -1,11 +1,18 @@ package testutil import ( + "encoding/json" + "fmt" + "github.com/stretchr/testify/suite" + tmcli "github.com/tendermint/tendermint/libs/cli" "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/testutil/cli" "github.com/cosmos/cosmos-sdk/testutil/network" sdk "github.com/cosmos/cosmos-sdk/types" + authcli "github.com/cosmos/cosmos-sdk/x/auth/client/cli" "github.com/cosmos/cosmos-sdk/x/sanction" "github.com/cosmos/cosmos-sdk/x/sanction/testutil" ) @@ -17,7 +24,9 @@ type IntegrationTestSuite struct { network *network.Network clientCtx client.Context - valAddr sdk.AccAddress + commonArgs []string + valAddr sdk.AccAddress + authority string sanctionGenesis *sanction.GenesisState } @@ -41,6 +50,14 @@ func (s *IntegrationTestSuite) SetupSuite() { s.clientCtx = s.network.Validators[0].ClientCtx s.valAddr = s.network.Validators[0].Address + + s.commonArgs = []string{ + fmt.Sprintf("--%s", flags.FlagFrom), s.valAddr.String(), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, s.bondCoins(10).String()), + } + } func (s *IntegrationTestSuite) TearDownSuite() { @@ -52,3 +69,55 @@ func (s *IntegrationTestSuite) TearDownSuite() { func (s *IntegrationTestSuite) assertErrorContents(theError error, contains []string, msgAndArgs ...interface{}) bool { return testutil.AssertErrorContents(s.T(), theError, contains, msgAndArgs...) } + +// bondCoin creates an sdk.Coin with the bond-denom in the amount provided. +func (s *IntegrationTestSuite) bondCoin(amt int64) sdk.Coin { + return sdk.NewInt64Coin(s.cfg.BondDenom, amt) +} + +// bondCoins creates an sdk.Coins with the bond-denom in the amount provided. +func (s *IntegrationTestSuite) bondCoins(amt int64) sdk.Coins { + return sdk.NewCoins(s.bondCoin(amt)) +} + +// appendCommonFlagsTo adds this suite's common flags to the end of the provided arguments. +func (s *IntegrationTestSuite) appendCommonArgsTo(args ...string) []string { + return append(args, s.commonArgs...) +} + +func (s *IntegrationTestSuite) getAuthority() string { + if len(s.authority) > 0 { + return s.authority + } + args := []string{"gov", "--" + tmcli.OutputFlag, "json"} + outBW, err := cli.ExecTestCLICmd(s.clientCtx, authcli.QueryModuleAccountByNameCmd(), args) + s.Require().NoError(err, "ExecTestCLICmd q auth module-account gov") + outBz := outBW.Bytes() + s.T().Logf("q auth module-account gov output:\n%s", string(outBz)) + // example output: + // { + // "account": { + // "@type": "/cosmos.auth.v1beta1.ModuleAccount", + // "base_account": { + // "address": "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn", + // "pub_key": null, + // "account_number": "9", + // "sequence": "0" + // }, + // "name": "gov", + // "permissions": ["burner"] + // } + // } + var output map[string]json.RawMessage + err = json.Unmarshal(outBz, &output) + s.Require().NoError(err, "Unmarshal output json") + var account map[string]json.RawMessage + err = json.Unmarshal(output["account"], &account) + s.Require().NoError(err, "Unmarshal account") + var baseAccount map[string]string + err = json.Unmarshal(account["base_account"], &baseAccount) + s.Require().NoError(err, "Unmarshal base_account") + s.authority = baseAccount["address"] + s.T().Logf("authority: %q", s.authority) + return s.authority +} diff --git a/x/sanction/client/testutil/tx_test.go b/x/sanction/client/testutil/tx_test.go new file mode 100644 index 000000000000..036bcebc9d4f --- /dev/null +++ b/x/sanction/client/testutil/tx_test.go @@ -0,0 +1,366 @@ +package testutil + +import ( + "github.com/cosmos/cosmos-sdk/client/flags" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/testutil/cli" + sdk "github.com/cosmos/cosmos-sdk/types" + govcli "github.com/cosmos/cosmos-sdk/x/gov/client/cli" + govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + "github.com/cosmos/cosmos-sdk/x/sanction" + client "github.com/cosmos/cosmos-sdk/x/sanction/client/cli" +) + +// assertGovPropMsg gets a gov prop and makes sure it has one specific message. +func (s *IntegrationTestSuite) assertGovPropMsg(propID string, msg sdk.Msg) bool { + s.T().Helper() + if msg == nil { + return true + } + + if !s.Assert().NotEmpty(propID, "proposal id") { + return false + } + expPropMsgAny, err := codectypes.NewAnyWithValue(msg) + if !s.Assert().NoError(err, "NewAnyWithValue on %T", msg) { + return false + } + + getPropCmd := govcli.GetCmdQueryProposal() + propOutBW, err := cli.ExecTestCLICmd(s.clientCtx, getPropCmd, []string{propID, "--output", "json"}) + propOut := propOutBW.String() + s.T().Logf("Query proposal %s output:\n%s", propID, propOut) + if !s.Assert().NoError(err, "GetCmdQueryProposal error") { + return false + } + + var prop govv1.Proposal + err = s.clientCtx.Codec.UnmarshalJSON([]byte(propOut), &prop) + if !s.Assert().NoError(err, "UnmarshalJSON on proposal response") { + return false + } + if !s.Assert().Len(prop.Messages, 1, "number of messages in proposal") { + return false + } + if !s.Assert().Equal(expPropMsgAny, prop.Messages[0], "the message in the proposal") { + return false + } + + return true +} + +// findProposalID looks through the provided response to find a governance proposal id. +// If one is found, it's returned (as a string). Otherwise, an empty string is returned. +func (s *IntegrationTestSuite) findProposalID(resp *sdk.TxResponse) string { + for _, event := range resp.Events { + if event.Type == "submit_proposal" { + for _, attr := range event.Attributes { + if string(attr.Key) == "proposal_id" { + return string(attr.Value) + } + } + } + } + return "" +} + +func (s *IntegrationTestSuite) TestTxSanctionCmd() { + authority := s.getAuthority() + addr1 := sdk.AccAddress("1_address_test_test_").String() + addr2 := sdk.AccAddress("2_address_test_test_").String() + + tests := []struct { + name string + args []string + expErr []string + expPropMsg *sanction.MsgSanction + }{ + { + name: "no addresses given", + args: []string{}, + expErr: []string{"requires at least 1 arg(s), only received 0"}, + }, + { + name: "one address good", + args: []string{addr1}, + expPropMsg: &sanction.MsgSanction{ + Addresses: []string{addr1}, + Authority: authority, + }, + }, + { + name: "one address bad", + args: []string{"thisis1addrthatisbad"}, + expErr: []string{"addresses[0]", `"thisis1addrthatisbad"`, "decoding bech32 failed"}, + }, + { + name: "two addresses first bad", + args: []string{"another1badaddr", addr2}, + expErr: []string{"addresses[0]", `"another1badaddr"`, "decoding bech32 failed"}, + }, + { + name: "two addresses second bad", + args: []string{addr1, "athird1badaddress"}, + expErr: []string{"addresses[1]", `"athird1badaddress"`, "decoding bech32 failed"}, + }, + { + name: "two addresses good", + args: []string{addr1, addr2}, + expPropMsg: &sanction.MsgSanction{ + Addresses: []string{addr1, addr2}, + Authority: authority, + }, + }, + { + name: "bad authority", + args: []string{addr1, "--" + flags.FlagAuthority, "bad1auth34sd2"}, + expErr: []string{"authority", `"bad1auth34sd2"`, "decoding bech32 failed"}, + }, + { + name: "bad deposit", + args: []string{addr1, "--" + govcli.FlagDeposit, "notcoins"}, + expErr: []string{"invalid deposit", "notcoins"}, + }, + } + + for _, tc := range tests { + s.Run(tc.name, func() { + cmd := client.TxSanctionCmd() + cmdFuncName := "TxSanctionCmd" + args := s.appendCommonArgsTo(tc.args...) + + outBW, err := cli.ExecTestCLICmd(s.clientCtx, cmd, args) + out := outBW.String() + s.T().Logf("Output:\n%s", out) + s.assertErrorContents(err, tc.expErr, "%s error", cmdFuncName) + for _, expErr := range tc.expErr { + s.Assert().Contains(out, expErr, "%s output with error", cmdFuncName) + } + + var propID string + if len(tc.expErr) == 0 { + var txResp sdk.TxResponse + err = s.clientCtx.Codec.UnmarshalJSON([]byte(out), &txResp) + if s.Assert().NoError(err, "UnmarshalJSON on %s", cmdFuncName) { + s.Assert().Equal(0, int(txResp.Code), "%s response code", cmdFuncName) + } + propID = s.findProposalID(&txResp) + } + + if tc.expPropMsg != nil { + s.assertGovPropMsg(propID, tc.expPropMsg) + } + }) + } +} + +func (s *IntegrationTestSuite) TestTxUnsanctionCmd() { + authority := s.getAuthority() + addr1 := sdk.AccAddress("1_address_untest____").String() + addr2 := sdk.AccAddress("2_address_untest____").String() + + tests := []struct { + name string + args []string + expErr []string + expPropMsg *sanction.MsgUnsanction + }{ + { + name: "no addresses given", + args: []string{}, + expErr: []string{"requires at least 1 arg(s), only received 0"}, + }, + { + name: "one address good", + args: []string{addr1}, + expPropMsg: &sanction.MsgUnsanction{ + Addresses: []string{addr1}, + Authority: authority, + }, + }, + { + name: "one address bad", + args: []string{"thisis1addrthatisbad"}, + expErr: []string{"addresses[0]", `"thisis1addrthatisbad"`, "decoding bech32 failed"}, + }, + { + name: "two addresses first bad", + args: []string{"another1badaddr", addr2}, + expErr: []string{"addresses[0]", `"another1badaddr"`, "decoding bech32 failed"}, + }, + { + name: "two addresses second bad", + args: []string{addr1, "athird1badaddress"}, + expErr: []string{"addresses[1]", `"athird1badaddress"`, "decoding bech32 failed"}, + }, + { + name: "two addresses good", + args: []string{addr1, addr2}, + expPropMsg: &sanction.MsgUnsanction{ + Addresses: []string{addr1, addr2}, + Authority: authority, + }, + }, + { + name: "bad authority", + args: []string{addr1, "--" + flags.FlagAuthority, "bad1auth34sd2"}, + expErr: []string{"authority", `"bad1auth34sd2"`, "decoding bech32 failed"}, + }, + { + name: "bad deposit", + args: []string{addr1, "--" + govcli.FlagDeposit, "notcoins"}, + expErr: []string{"invalid deposit", "notcoins"}, + }, + } + + for _, tc := range tests { + s.Run(tc.name, func() { + cmd := client.TxUnsanctionCmd() + cmdFuncName := "TxUnsanctionCmd" + args := s.appendCommonArgsTo(tc.args...) + + outBW, err := cli.ExecTestCLICmd(s.clientCtx, cmd, args) + out := outBW.String() + s.T().Logf("Output:\n%s", out) + s.assertErrorContents(err, tc.expErr, "%s error", cmdFuncName) + for _, expErr := range tc.expErr { + s.Assert().Contains(out, expErr, "%s output with error", cmdFuncName) + } + + var propID string + if len(tc.expErr) == 0 { + var txResp sdk.TxResponse + err = s.clientCtx.Codec.UnmarshalJSON([]byte(out), &txResp) + if s.Assert().NoError(err, "UnmarshalJSON on %s", cmdFuncName) { + s.Assert().Equal(0, int(txResp.Code), "%s response code", cmdFuncName) + } + propID = s.findProposalID(&txResp) + } + + if tc.expPropMsg != nil { + s.assertGovPropMsg(propID, tc.expPropMsg) + } + }) + } +} + +func (s *IntegrationTestSuite) TestTxUpdateParamsCmd() { + authority := s.getAuthority() + + tests := []struct { + name string + args []string + expErr []string + expPropMsg *sanction.MsgUpdateParams + }{ + { + name: "no args", + args: []string{}, + expErr: []string{"accepts 2 arg(s), received 0"}, + }, + { + name: "one arg", + args: []string{"arg1"}, + expErr: []string{"accepts 2 arg(s), received 1"}, + }, + { + name: "three args", + args: []string{"arg1", "arg2", "arg3"}, + expErr: []string{"accepts 2 arg(s), received 3"}, + }, + { + name: "coins coins", + args: []string{"1acoin", "2bcoin"}, + expPropMsg: &sanction.MsgUpdateParams{ + Params: &sanction.Params{ + ImmediateSanctionMinDeposit: sdk.NewCoins(sdk.NewInt64Coin("acoin", 1)), + ImmediateUnsanctionMinDeposit: sdk.NewCoins(sdk.NewInt64Coin("bcoin", 2)), + }, + Authority: authority, + }, + }, + { + name: "empty coins", + args: []string{"", "3ccoin"}, + expPropMsg: &sanction.MsgUpdateParams{ + Params: &sanction.Params{ + ImmediateSanctionMinDeposit: nil, + ImmediateUnsanctionMinDeposit: sdk.NewCoins(sdk.NewInt64Coin("ccoin", 3)), + }, + Authority: authority, + }, + }, + { + name: "coins empty", + args: []string{"4dcoin", ""}, + expPropMsg: &sanction.MsgUpdateParams{ + Params: &sanction.Params{ + ImmediateSanctionMinDeposit: sdk.NewCoins(sdk.NewInt64Coin("dcoin", 4)), + ImmediateUnsanctionMinDeposit: nil, + }, + Authority: authority, + }, + }, + { + name: "empty empty", + args: []string{"", ""}, + expPropMsg: &sanction.MsgUpdateParams{ + Params: &sanction.Params{ + ImmediateSanctionMinDeposit: nil, + ImmediateUnsanctionMinDeposit: nil, + }, + Authority: authority, + }, + }, + { + name: "bad good", + args: []string{"firscoinsbad", "5ecoin"}, + expErr: []string{"invalid immediate_sanction_min_deposit", `"firscoinsbad"`}, + }, + { + name: "good bad", + args: []string{"6fcoin", "secondcoinsbad"}, + expErr: []string{"invalid immediate_unsanction_min_deposit", `"secondcoinsbad"`}, + }, + { + name: "bad authority", + args: []string{"", "", "--" + flags.FlagAuthority, "bad1auth34sd2"}, + expErr: []string{"authority", `"bad1auth34sd2"`, "decoding bech32 failed"}, + }, + { + name: "bad deposit", + args: []string{"", "", "--" + govcli.FlagDeposit, "notcoins"}, + expErr: []string{"invalid deposit", "notcoins"}, + }, + } + + for _, tc := range tests { + s.Run(tc.name, func() { + cmd := client.TxUpdateParamsCmd() + cmdFuncName := "TxUpdateParamsCmd" + args := s.appendCommonArgsTo(tc.args...) + + outBW, err := cli.ExecTestCLICmd(s.clientCtx, cmd, args) + out := outBW.String() + s.T().Logf("Output:\n%s", out) + s.assertErrorContents(err, tc.expErr, "%s error", cmdFuncName) + for _, expErr := range tc.expErr { + s.Assert().Contains(out, expErr, "%s output with error", cmdFuncName) + } + + var propID string + if len(tc.expErr) == 0 { + var txResp sdk.TxResponse + err = s.clientCtx.Codec.UnmarshalJSON([]byte(out), &txResp) + if s.Assert().NoError(err, "UnmarshalJSON on %s", cmdFuncName) { + s.Assert().Equal(0, int(txResp.Code), "%s response code", cmdFuncName) + } + propID = s.findProposalID(&txResp) + } + + if tc.expPropMsg != nil { + s.assertGovPropMsg(propID, tc.expPropMsg) + } + }) + } +} diff --git a/x/sanction/module/module.go b/x/sanction/module/module.go index 92df492fd5e7..2d3ef01558ef 100644 --- a/x/sanction/module/module.go +++ b/x/sanction/module/module.go @@ -77,7 +77,7 @@ func (a AppModuleBasic) GetQueryCmd() *cobra.Command { // GetTxCmd returns the transaction commands for the sanction module func (a AppModuleBasic) GetTxCmd() *cobra.Command { - return nil + return cli.TxCmd() } // RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the sanction module. diff --git a/x/staking/genesis.go b/x/staking/genesis.go index 5b94b8adf48b..7bed1c10e3d9 100644 --- a/x/staking/genesis.go +++ b/x/staking/genesis.go @@ -12,14 +12,16 @@ import ( ) // WriteValidators returns a slice of bonded genesis validators. -func WriteValidators(ctx sdk.Context, keeper keeper.Keeper) (vals []tmtypes.GenesisValidator, err error) { +func WriteValidators(ctx sdk.Context, keeper keeper.Keeper) (vals []tmtypes.GenesisValidator, returnErr error) { keeper.IterateLastValidators(ctx, func(_ int64, validator types.ValidatorI) (stop bool) { pk, err := validator.ConsPubKey() if err != nil { + returnErr = err return true } tmPk, err := cryptocodec.ToTmPubKeyInterface(pk) if err != nil { + returnErr = err return true }