diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index dab6899..830b2e3 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,4 +4,4 @@ # Primary repo maintainers -* @davidterpay @nivasan1 @aljo242 @Eric-Warehime +* @aljo242 @Eric-Warehime @technicallyty @wesl-ee diff --git a/go.mod b/go.mod index 0fe4e70..7723bb4 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/skip-mev/feemarket -go 1.22.3 +go 1.22.6 require ( cosmossdk.io/api v0.3.1 @@ -12,8 +12,9 @@ require ( github.com/client9/misspell v0.3.4 github.com/cometbft/cometbft v0.37.5 github.com/cometbft/cometbft-db v0.8.0 + github.com/cosmos/cosmos-db v1.0.2 github.com/cosmos/cosmos-proto v1.0.0-beta.5 - github.com/cosmos/cosmos-sdk v0.47.12 + github.com/cosmos/cosmos-sdk v0.47.13 github.com/cosmos/gogoproto v1.5.0 github.com/golang/protobuf v1.5.4 github.com/golangci/golangci-lint v1.59.1 @@ -23,9 +24,9 @@ require ( github.com/spf13/viper v1.19.0 github.com/stretchr/testify v1.9.0 github.com/vektra/mockery/v2 v2.43.2 - golang.org/x/tools v0.22.0 + golang.org/x/tools v0.24.0 google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3 - google.golang.org/grpc v1.64.0 + google.golang.org/grpc v1.65.0 google.golang.org/protobuf v1.34.2 mvdan.cc/gofumpt v0.6.0 pgregory.net/rapid v1.1.0 @@ -52,6 +53,7 @@ require ( github.com/BurntSushi/toml v1.4.0 // indirect github.com/ChainSafe/go-schnorrkel v1.0.0 // indirect github.com/Crocmagnon/fatcontext v0.2.2 // indirect + github.com/DataDog/zstd v1.4.5 // indirect github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0 // indirect github.com/Masterminds/semver/v3 v3.2.1 // indirect @@ -79,7 +81,7 @@ require ( github.com/ccojocar/zxcvbn-go v1.0.2 // indirect github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cespare/xxhash v1.1.0 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/charithe/durationcheck v0.0.10 // indirect github.com/chavacava/garif v0.1.0 // indirect github.com/chigopher/pathlib v0.19.1 // indirect @@ -88,7 +90,9 @@ require ( github.com/cockroachdb/apd/v2 v2.0.2 // indirect github.com/cockroachdb/errors v1.11.1 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect + github.com/cockroachdb/pebble v1.1.0 // indirect github.com/cockroachdb/redact v1.1.5 // indirect + github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect github.com/coinbase/rosetta-sdk-go/types v1.0.0 // indirect github.com/confio/ics23/go v0.9.0 // indirect github.com/cosmos/btcutil v1.0.5 // indirect @@ -170,7 +174,7 @@ require ( github.com/gtank/merlin v0.1.1 // indirect github.com/gtank/ristretto255 v0.1.2 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-getter v1.7.1 // indirect + github.com/hashicorp/go-getter v1.7.5 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-safetemp v1.0.0 // indirect github.com/hashicorp/go-version v1.7.0 // indirect @@ -206,7 +210,7 @@ require ( github.com/leonklingele/grouper v1.1.2 // indirect github.com/lib/pq v1.10.9 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect - github.com/linxGnu/grocksdb v1.8.4 // indirect + github.com/linxGnu/grocksdb v1.8.14 // indirect github.com/lufeee/execinquery v1.2.1 // indirect github.com/macabu/inamedparam v0.1.3 // indirect github.com/magiconair/properties v1.8.7 // indirect @@ -308,20 +312,20 @@ require ( go.uber.org/automaxprocs v1.5.3 // indirect go.uber.org/multierr v1.10.0 // indirect go.uber.org/zap v1.24.0 // indirect - golang.org/x/crypto v0.24.0 // indirect + golang.org/x/crypto v0.26.0 // indirect golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 // indirect golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f // indirect - golang.org/x/mod v0.18.0 // indirect - golang.org/x/net v0.26.0 // indirect + golang.org/x/mod v0.20.0 // indirect + golang.org/x/net v0.28.0 // indirect golang.org/x/oauth2 v0.20.0 // indirect - golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/term v0.21.0 // indirect - golang.org/x/text v0.16.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.23.0 // indirect + golang.org/x/term v0.23.0 // indirect + golang.org/x/text v0.17.0 // indirect golang.org/x/time v0.5.0 // indirect google.golang.org/api v0.180.0 // indirect google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240709173604-40e1e62336c5 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index e15df16..23b8cea 100644 --- a/go.sum +++ b/go.sum @@ -438,6 +438,8 @@ github.com/ChainSafe/go-schnorrkel v1.0.0/go.mod h1:dpzHYVxLZcp8pjlV+O+UR8K0Hp/z github.com/Crocmagnon/fatcontext v0.2.2 h1:OrFlsDdOj9hW/oBEJBNSuH7QWf+E9WPVHw+x52bXVbk= github.com/Crocmagnon/fatcontext v0.2.2/go.mod h1:WSn/c/+MMNiD8Pri0ahRj0o9jVpeowzavOQplBJw6u0= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= +github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0 h1:/fTUt5vmbkAcMBt4YQiuC23cV0kEsN1MVMNqeOW43cU= @@ -546,8 +548,9 @@ github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91 github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/charithe/durationcheck v0.0.10 h1:wgw73BiocdBDQPik+zcEoBG/ob8uyBHf2iyoHGPf5w4= github.com/charithe/durationcheck v0.0.10/go.mod h1:bCWXb7gYRysD1CU3C+u4ceO49LoGOY1C1L6uouGNreQ= github.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc= @@ -586,12 +589,18 @@ github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWH github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b8034E= github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZazG8= github.com/cockroachdb/errors v1.11.1/go.mod h1:8MUxA3Gi6b25tYlFEBGLf+D8aISL+M4MIpiWMSNRfxw= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/pebble v1.1.0 h1:pcFh8CdCIt2kmEpK0OIatq67Ln9uGDYY3d5XnE0LJG4= +github.com/cockroachdb/pebble v1.1.0/go.mod h1:sEHm5NOXxyiAoKWhoFxT8xMgd/f3RA6qUqQ1BXKrh2E= github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coinbase/rosetta-sdk-go/types v1.0.0 h1:jpVIwLcPoOeCR6o1tU+Xv7r5bMONNbHU7MuEHboiFuA= github.com/coinbase/rosetta-sdk-go/types v1.0.0/go.mod h1:eq7W2TMRH22GTW0N0beDnN931DW0/WOI1R2sdHNHG4c= @@ -611,10 +620,12 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk= github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis= +github.com/cosmos/cosmos-db v1.0.2 h1:hwMjozuY1OlJs/uh6vddqnk9j7VamLv+0DBlbEXbAKs= +github.com/cosmos/cosmos-db v1.0.2/go.mod h1:Z8IXcFJ9PqKK6BIsVOB3QXtkKoqUOp1vRvPT39kOXEA= github.com/cosmos/cosmos-proto v1.0.0-beta.5 h1:eNcayDLpip+zVLRLYafhzLvQlSmyab+RC5W7ZfmxJLA= github.com/cosmos/cosmos-proto v1.0.0-beta.5/go.mod h1:hQGLpiIUloJBMdQMMWb/4wRApmI9hjHH05nefC0Ojec= -github.com/cosmos/cosmos-sdk v0.47.12 h1:KOZHAVWrcilHywBN/FabBaXbDFMzoFmtdX0hqy5Ory8= -github.com/cosmos/cosmos-sdk v0.47.12/go.mod h1:ADjORYzUQqQv/FxDi0H0K5gW/rAk1CiDR3ZKsExfJV0= +github.com/cosmos/cosmos-sdk v0.47.13 h1:9d57rl2ilSgc8a6u1JAulqNX/E5w8lbqbRe3NON3Jb4= +github.com/cosmos/cosmos-sdk v0.47.13/go.mod h1:pYMzhTfKFn9AJB5X64Epwe9NgYk0y3v7XN8Ks5xqWoo= github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= @@ -999,8 +1010,8 @@ github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-getter v1.7.1 h1:SWiSWN/42qdpR0MdhaOc/bLR48PLuP1ZQtYLRlM69uY= -github.com/hashicorp/go-getter v1.7.1/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= +github.com/hashicorp/go-getter v1.7.5 h1:dT58k9hQ/vbxNMwoI5+xFYAJuv6152UNvdHokfI5wE4= +github.com/hashicorp/go-getter v1.7.5/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= @@ -1139,8 +1150,8 @@ github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6 github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= -github.com/linxGnu/grocksdb v1.8.4 h1:ZMsBpPpJNtRLHiKKp0mI7gW+NT4s7UgfD5xHxx1jVRo= -github.com/linxGnu/grocksdb v1.8.4/go.mod h1:xZCIb5Muw+nhbDK4Y5UJuOrin5MceOuiXkVUR7vp4WY= +github.com/linxGnu/grocksdb v1.8.14 h1:HTgyYalNwBSG/1qCQUIott44wU5b2Y9Kr3z7SK5OfGQ= +github.com/linxGnu/grocksdb v1.8.14/go.mod h1:QYiYypR2d4v63Wj1adOOfzglnoII0gLj3PNh4fZkcFA= github.com/lufeee/execinquery v1.2.1 h1:hf0Ems4SHcUGBxpGN7Jz78z1ppVkP/837ZlETPCEtOM= github.com/lufeee/execinquery v1.2.1/go.mod h1:EC7DrEKView09ocscGHC+apXMIaorh4xqSxS/dy8SbM= github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= @@ -1611,8 +1622,8 @@ golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/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.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 h1:Jvc7gsqn21cJHCmAWx0LiimpP18LZmUxkT5Mp7EZ1mI= golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= @@ -1645,8 +1656,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= -golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= +golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1715,8 +1726,8 @@ golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= 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= @@ -1761,8 +1772,8 @@ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1868,8 +1879,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= +golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1878,8 +1889,8 @@ golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= -golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= -golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= 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= @@ -1895,8 +1906,8 @@ golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= 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= @@ -1980,8 +1991,8 @@ golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= -golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= +golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= +golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -2175,8 +2186,8 @@ google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda h1:wu/KJm9KJwpfHWh google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda/go.mod h1:g2LLCvCeCSir/JJSWosk19BR4NVxGqHUC6rxIRsd7Aw= google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3 h1:QW9+G6Fir4VcRXVH8x3LilNAb6cxBGLa6+GM4hRwexE= google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3/go.mod h1:kdrSS/OiLkPrNUpzD4aHgCq2rVuC/YRxok32HXZ4vRE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 h1:1GBuWVLM/KMVUv1t1En5Gs+gFZCNd360GGb4sSxtrhU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240709173604-40e1e62336c5 h1:SbSDUWW1PAO24TNpLdeheoYPd7kllICcLU52x6eD4kQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240709173604-40e1e62336c5/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -2221,8 +2232,8 @@ google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCD google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= -google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= -google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= diff --git a/tests/app/ante.go b/tests/app/ante.go index 69d0e4d..158c787 100644 --- a/tests/app/ante.go +++ b/tests/app/ante.go @@ -13,6 +13,7 @@ import ( // AnteHandlerOptions are the options required for constructing an SDK AnteHandler with the fee market injected. type AnteHandlerOptions struct { BaseOptions authante.HandlerOptions + BankKeeper feemarketante.BankKeeper AccountKeeper feemarketante.AccountKeeper FeeMarketKeeper feemarketante.FeeMarketKeeper } @@ -26,7 +27,7 @@ func NewAnteHandler(options AnteHandlerOptions) (sdk.AnteHandler, error) { } if options.BaseOptions.BankKeeper == nil { - return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "bank keeper is required for ante builder") + return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "base options bank keeper is required for ante builder") } if options.BaseOptions.SignModeHandler == nil { @@ -37,6 +38,10 @@ func NewAnteHandler(options AnteHandlerOptions) (sdk.AnteHandler, error) { return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "feemarket keeper is required for ante builder") } + if options.BankKeeper == nil { + return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "bank keeper keeper is required for ante builder") + } + anteDecorators := []sdk.AnteDecorator{ authante.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first authante.NewExtensionOptionsDecorator(options.BaseOptions.ExtensionOptionChecker), @@ -45,6 +50,9 @@ func NewAnteHandler(options AnteHandlerOptions) (sdk.AnteHandler, error) { authante.NewValidateMemoDecorator(options.AccountKeeper), authante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper), feemarketante.NewFeeMarketCheckDecorator( // fee market check replaces fee deduct decorator + options.AccountKeeper, + options.BankKeeper, + options.BaseOptions.FeegrantKeeper, options.FeeMarketKeeper, authante.NewDeductFeeDecorator( options.AccountKeeper, diff --git a/tests/app/app.go b/tests/app/app.go index ab3de82..5edb4b7 100644 --- a/tests/app/app.go +++ b/tests/app/app.go @@ -268,6 +268,7 @@ func New( anteOptions := AnteHandlerOptions{ BaseOptions: anteHandlerOptions, AccountKeeper: app.AccountKeeper, + BankKeeper: app.BankKeeper, FeeMarketKeeper: &app.FeeMarketKeeper, } anteHandler, err := NewAnteHandler(anteOptions) @@ -278,7 +279,6 @@ func New( postHandlerOptions := PostHandlerOptions{ AccountKeeper: app.AccountKeeper, BankKeeper: app.BankKeeper, - FeeGrantKeeper: app.FeeGrantKeeper, FeeMarketKeeper: &app.FeeMarketKeeper, } postHandler, err := NewPostHandler(postHandlerOptions) diff --git a/tests/app/post.go b/tests/app/post.go index a273c83..1ec96bc 100644 --- a/tests/app/post.go +++ b/tests/app/post.go @@ -13,7 +13,6 @@ type PostHandlerOptions struct { AccountKeeper feemarketpost.AccountKeeper BankKeeper feemarketpost.BankKeeper FeeMarketKeeper feemarketpost.FeeMarketKeeper - FeeGrantKeeper feemarketpost.FeeGrantKeeper } // NewPostHandler returns a PostHandler chain with the fee deduct decorator. @@ -34,7 +33,6 @@ func NewPostHandler(options PostHandlerOptions) (sdk.PostHandler, error) { feemarketpost.NewFeeMarketDeductDecorator( options.AccountKeeper, options.BankKeeper, - options.FeeGrantKeeper, options.FeeMarketKeeper, ), } diff --git a/tests/e2e/setup.go b/tests/e2e/setup.go index 6f24ebc..fd8440d 100644 --- a/tests/e2e/setup.go +++ b/tests/e2e/setup.go @@ -75,7 +75,7 @@ func (s *TestSuite) QueryParams() types.Params { cc, err := grpc.NewClient(grpcAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) s.Require().NoError(err) - // create the oracle client + // create the feemarket client c := types.NewQueryClient(cc) resp, err := c.Params(context.Background(), &types.ParamsRequest{}) @@ -84,6 +84,29 @@ func (s *TestSuite) QueryParams() types.Params { return resp.Params } +func (s *TestSuite) QueryBalance(user ibc.Wallet) sdk.Coin { + s.T().Helper() + + // get grpc address + grpcAddr := s.chain.GetHostGRPCAddress() + + // create the client + cc, err := grpc.NewClient(grpcAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) + s.Require().NoError(err) + + // create the bank client + c := banktypes.NewQueryClient(cc) + + resp, err := c.Balance(context.Background(), &banktypes.QueryBalanceRequest{ + Address: user.FormattedAddress(), + Denom: defaultDenom, + }) + s.Require().NoError(err) + s.Require().NotNil(*resp.Balance) + + return *resp.Balance +} + func (s *TestSuite) QueryState() types.State { s.T().Helper() @@ -93,7 +116,7 @@ func (s *TestSuite) QueryState() types.State { cc, err := grpc.NewClient(grpcAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) s.Require().NoError(err) - // create the oracle client + // create the feemarket client c := types.NewQueryClient(cc) resp, err := c.State(context.Background(), &types.StateRequest{}) @@ -112,7 +135,7 @@ func (s *TestSuite) QueryDefaultGasPrice() sdk.DecCoin { cc, err := grpc.NewClient(grpcAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) s.Require().NoError(err) - // create the oracle client + // create the feemarket client c := types.NewQueryClient(cc) resp, err := c.GasPrice(context.Background(), &types.GasPriceRequest{ @@ -339,13 +362,37 @@ func (s *TestSuite) SendCoinsMultiBroadcast(ctx context.Context, sender, receive } } - tx := s.CreateTx(cc, sender, fees.String(), gas, msgs...) + tx := s.CreateTx(cc, sender, fees.String(), gas, false, msgs...) // get an rpc endpoint for the chain c := cc.Nodes()[0].Client return c.BroadcastTxCommit(ctx, tx) } +func (s *TestSuite) SendCoinsMultiBroadcastAsync(ctx context.Context, sender, receiver ibc.Wallet, amt, fees sdk.Coins, + gas int64, numMsg int, bumpSequence bool, +) (*coretypes.ResultBroadcastTx, error) { + cc, ok := s.chain.(*cosmos.CosmosChain) + if !ok { + panic("unable to assert ibc.Chain as CosmosChain") + } + + msgs := make([]sdk.Msg, numMsg) + for i := 0; i < numMsg; i++ { + msgs[i] = &banktypes.MsgSend{ + FromAddress: sender.FormattedAddress(), + ToAddress: receiver.FormattedAddress(), + Amount: amt, + } + } + + tx := s.CreateTx(cc, sender, fees.String(), gas, bumpSequence, msgs...) + + // get an rpc endpoint for the chain + c := cc.Nodes()[0].Client + return c.BroadcastTxAsync(ctx, tx) +} + // SendCoins creates a executes a SendCoins message and broadcasts the transaction. func (s *TestSuite) SendCoins(ctx context.Context, keyName, sender, receiver string, amt, fees sdk.Coins, gas int64) (string, error) { cc, ok := s.chain.(*cosmos.CosmosChain) @@ -388,7 +435,14 @@ func (s *TestSuite) GetAndFundTestUserWithMnemonic( return nil, fmt.Errorf("failed to get source user wallet: %w", err) } - _, err = s.SendCoins( + s.FundUser(ctx, chain, amount, user) + return user, nil +} + +func (s *TestSuite) FundUser(ctx context.Context, chain ibc.Chain, amount int64, user ibc.Wallet) { + chainCfg := chain.Config() + + _, err := s.SendCoins( ctx, interchaintest.FaucetAccountKeyName, interchaintest.FaucetAccountKeyName, @@ -398,7 +452,6 @@ func (s *TestSuite) GetAndFundTestUserWithMnemonic( 1000000, ) s.Require().NoError(err, "failed to get funds from faucet") - return user, nil } // GetAndFundTestUsers generates and funds chain users with the native chain denom. @@ -438,7 +491,9 @@ func (s *TestSuite) ExecTx(ctx context.Context, chain *cosmos.CosmosChain, keyNa } // CreateTx creates a new transaction to be signed by the given user, including a provided set of messages -func (s *TestSuite) CreateTx(chain *cosmos.CosmosChain, user cosmos.User, fee string, gas int64, msgs ...sdk.Msg) []byte { +func (s *TestSuite) CreateTx(chain *cosmos.CosmosChain, user cosmos.User, fee string, gas int64, + bumpSequence bool, msgs ...sdk.Msg, +) []byte { bc := cosmos.NewBroadcaster(s.T(), chain) ctx := context.Background() @@ -462,6 +517,9 @@ func (s *TestSuite) CreateTx(chain *cosmos.CosmosChain, user cosmos.User, fee st // update sequence number txf = txf.WithSequence(txf.Sequence()) + if bumpSequence { + txf = txf.WithSequence(txf.Sequence() + 1) + } // sign the tx txBuilder, err := txf.BuildUnsignedTx(msgs...) diff --git a/tests/e2e/suite.go b/tests/e2e/suite.go index 143796f..8fcab8c 100644 --- a/tests/e2e/suite.go +++ b/tests/e2e/suite.go @@ -269,11 +269,13 @@ func (s *TestSuite) TestQueryGasPrice() { // TestSendTxDecrease tests that the feemarket will decrease until it hits the min gas price // when gas utilization is below the target block utilization. func (s *TestSuite) TestSendTxDecrease() { - // cast chain to cosmos-chain - cosmosChain, ok := s.chain.(*cosmos.CosmosChain) - s.Require().True(ok) + cc, ok := s.chain.(*cosmos.CosmosChain) + if !ok { + panic("not cosmos chain") + } + // get nodes - nodes := cosmosChain.Nodes() + nodes := cc.Nodes() s.Require().True(len(nodes) > 0) params := s.QueryParams() @@ -310,64 +312,39 @@ func (s *TestSuite) TestSendTxDecrease() { break case <-time.After(100 * time.Millisecond): - wg := sync.WaitGroup{} + wg := &sync.WaitGroup{} wg.Add(3) - go func() { + smallSend := func(wg *sync.WaitGroup, userA, userB ibc.Wallet) { defer wg.Done() txResp, err := s.SendCoinsMultiBroadcast( context.Background(), - s.user1, - s.user2, - sdk.NewCoins(sdk.NewCoin(cosmosChain.Config().Denom, math.NewInt(sendAmt))), + userA, + userB, + sdk.NewCoins(sdk.NewCoin(s.chain.Config().Denom, math.NewInt(sendAmt))), minBaseFeeCoins, gas, s.txConfig.SmallSendsNum, ) - s.Require().NoError(err, txResp) - s.Require().Equal(uint32(0), txResp.CheckTx.Code, txResp.CheckTx) - s.Require().Equal(uint32(0), txResp.DeliverTx.Code, txResp.DeliverTx) - }() + if err != nil { + s.T().Log(err) + } else if txResp != nil && txResp.CheckTx.Code != 0 { + s.T().Log(txResp.CheckTx) + } + } - go func() { - defer wg.Done() - txResp, err := s.SendCoinsMultiBroadcast( - context.Background(), - s.user3, - s.user2, - sdk.NewCoins(sdk.NewCoin(cosmosChain.Config().Denom, math.NewInt(sendAmt))), - minBaseFeeCoins, - gas, - s.txConfig.SmallSendsNum, - ) - s.Require().NoError(err, txResp) - s.Require().Equal(uint32(0), txResp.CheckTx.Code, txResp.CheckTx) - s.Require().Equal(uint32(0), txResp.DeliverTx.Code, txResp.DeliverTx) - }() + go smallSend(wg, s.user1, s.user2) + go smallSend(wg, s.user3, s.user2) + go smallSend(wg, s.user2, s.user1) - go func() { - defer wg.Done() - txResp, err := s.SendCoinsMultiBroadcast( - context.Background(), - s.user2, - s.user3, - sdk.NewCoins(sdk.NewCoin(cosmosChain.Config().Denom, math.NewInt(sendAmt))), - minBaseFeeCoins, - gas, - s.txConfig.SmallSendsNum, - ) - s.Require().NoError(err, txResp) - s.Require().Equal(uint32(0), txResp.CheckTx.Code, txResp.CheckTx) - s.Require().Equal(uint32(0), txResp.DeliverTx.Code, txResp.DeliverTx) - }() wg.Wait() } // wait for 5 blocks // query height - height, err := s.chain.(*cosmos.CosmosChain).Height(context.Background()) + height, err := s.chain.Height(context.Background()) s.Require().NoError(err) - s.WaitForHeight(s.chain.(*cosmos.CosmosChain), height+5) + s.WaitForHeight(cc, height+5) gasPrice := s.QueryDefaultGasPrice() s.T().Log("gas price", gasPrice.String()) @@ -382,11 +359,13 @@ func (s *TestSuite) TestSendTxDecrease() { // TestSendTxIncrease tests that the feemarket will increase // when gas utilization is above the target block utilization. func (s *TestSuite) TestSendTxIncrease() { - // cast chain to cosmos-chain - cosmosChain, ok := s.chain.(*cosmos.CosmosChain) - s.Require().True(ok) + cc, ok := s.chain.(*cosmos.CosmosChain) + if !ok { + panic("not cosmos chain") + } + // get nodes - nodes := cosmosChain.Nodes() + nodes := cc.Nodes() s.Require().True(len(nodes) > 0) params := s.QueryParams() @@ -426,73 +405,220 @@ func (s *TestSuite) TestSendTxIncrease() { // add headroom minBaseFeeCoins := sdk.NewCoins(sdk.NewCoin(minBaseFee.Denom, minBaseFee.Amount.Add(math.LegacyNewDec(10)).TruncateInt())) - _, err := s.chain.(*cosmos.CosmosChain).Height(context.Background()) - s.Require().NoError(err) - wg := sync.WaitGroup{} + wg := &sync.WaitGroup{} wg.Add(3) - go func() { + largeSend := func(wg *sync.WaitGroup, userA, userB ibc.Wallet) { defer wg.Done() txResp, err := s.SendCoinsMultiBroadcast( context.Background(), - s.user1, - s.user2, - sdk.NewCoins(sdk.NewCoin(cosmosChain.Config().Denom, math.NewInt(sendAmt))), + userA, + userB, + sdk.NewCoins(sdk.NewCoin(s.chain.Config().Denom, math.NewInt(sendAmt))), minBaseFeeCoins, gas, s.txConfig.LargeSendsNum, ) - s.Require().NoError(err, txResp) - s.Require().Equal(uint32(0), txResp.CheckTx.Code, txResp.CheckTx) - s.Require().Equal(uint32(0), txResp.DeliverTx.Code, txResp.DeliverTx) - }() + if err != nil { + s.T().Log(err) + } else if txResp != nil && txResp.CheckTx.Code != 0 { + s.T().Log(txResp.CheckTx) + } + } - go func() { - defer wg.Done() - txResp, err := s.SendCoinsMultiBroadcast( - context.Background(), - s.user3, - s.user2, - sdk.NewCoins(sdk.NewCoin(cosmosChain.Config().Denom, math.NewInt(sendAmt))), - minBaseFeeCoins, - gas, - s.txConfig.LargeSendsNum, - ) - s.Require().NoError(err, txResp) - s.Require().Equal(uint32(0), txResp.CheckTx.Code, txResp.CheckTx) - s.Require().Equal(uint32(0), txResp.DeliverTx.Code, txResp.DeliverTx) - }() + go largeSend(wg, s.user1, s.user2) + go largeSend(wg, s.user3, s.user2) + go largeSend(wg, s.user2, s.user1) - go func() { - defer wg.Done() - txResp, err := s.SendCoinsMultiBroadcast( - context.Background(), - s.user2, - s.user1, - sdk.NewCoins(sdk.NewCoin(cosmosChain.Config().Denom, math.NewInt(sendAmt))), - minBaseFeeCoins, - gas, - s.txConfig.LargeSendsNum, - ) - s.Require().NoError(err, txResp) - s.Require().Equal(uint32(0), txResp.CheckTx.Code, txResp.CheckTx) - s.Require().Equal(uint32(0), txResp.DeliverTx.Code, txResp.DeliverTx) - }() wg.Wait() } // wait for 5 blocks // query height - height, err := s.chain.(*cosmos.CosmosChain).Height(context.Background()) + height, err := s.chain.Height(context.Background()) s.Require().NoError(err) - s.WaitForHeight(s.chain.(*cosmos.CosmosChain), height+5) + s.WaitForHeight(cc, height+5) gasPrice := s.QueryDefaultGasPrice() s.T().Log("gas price", gasPrice.String()) amt, err := s.chain.GetBalance(context.Background(), s.user1.FormattedAddress(), gasPrice.Denom) s.Require().NoError(err) - s.Require().True(amt.LT(math.NewInt(initBalance)), amt) s.T().Log("balance:", amt.String()) }) } + +func (s *TestSuite) TestSendTxFailures() { + sendAmt := int64(100) + gas := int64(200000) + + cc, ok := s.chain.(*cosmos.CosmosChain) + if !ok { + panic("not cosmos chain") + } + + s.Run("submit tx with no gas attached", func() { + // send one tx with no gas or fee attached + txResp, err := s.SendCoinsMultiBroadcast( + context.Background(), + s.user1, + s.user3, + sdk.NewCoins(sdk.NewCoin(s.chain.Config().Denom, math.NewInt(sendAmt))), + sdk.NewCoins(), + 0, + 1, + ) + s.Require().NoError(err) + s.Require().NotNil(txResp) + s.Require().True(txResp.CheckTx.Code != 0) + s.T().Log(txResp.CheckTx.Log) + s.Require().Contains(txResp.CheckTx.Log, "out of gas") + }) + + s.Run("submit tx with no fee", func() { + txResp, err := s.SendCoinsMultiBroadcast( + context.Background(), + s.user1, + s.user3, + sdk.NewCoins(sdk.NewCoin(s.chain.Config().Denom, math.NewInt(sendAmt))), + sdk.NewCoins(), + gas, + 1, + ) + s.Require().NoError(err) + s.Require().NotNil(txResp) + s.Require().True(txResp.CheckTx.Code != 0) + s.T().Log(txResp.CheckTx.Log) + s.Require().Contains(txResp.CheckTx.Log, "no fee coin provided") + }) + + s.Run("fail a tx that uses full balance in fee - fail tx", func() { + balance := s.QueryBalance(s.user3) + + // send one tx with no gas or fee attached + txResp, err := s.SendCoinsMultiBroadcast( + context.Background(), + s.user3, + s.user1, + sdk.NewCoins(balance), + sdk.NewCoins(balance), + gas, + 1, + ) + s.Require().NoError(err) + s.Require().NotNil(txResp) + s.Require().True(txResp.CheckTx.Code == 0) + s.Require().True(txResp.DeliverTx.Code != 0) + s.T().Log(txResp.DeliverTx.Log) + s.Require().Contains(txResp.DeliverTx.Log, "insufficient funds") + + // ensure that balance is deducted for any tx passing checkTx + newBalance := s.QueryBalance(s.user3) + s.Require().True(newBalance.IsLT(balance), fmt.Sprintf("new balance: %d, original balance: %d", + balance.Amount.Int64(), + newBalance.Amount.Int64())) + }) + + s.Run("submit a tx for full balance - fail tx", func() { + balance := s.QueryBalance(s.user1) + + defaultGasPrice := s.QueryDefaultGasPrice() + minBaseFee := sdk.NewDecCoinFromDec(defaultGasPrice.Denom, defaultGasPrice.Amount.Mul(math.LegacyNewDec(gas))) + minBaseFeeCoins := sdk.NewCoins(sdk.NewCoin(minBaseFee.Denom, minBaseFee.Amount.TruncateInt().Add(math. + NewInt(100)))) + txResp, err := s.SendCoinsMultiBroadcast( + context.Background(), + s.user1, + s.user3, + sdk.NewCoins(balance), + minBaseFeeCoins, + gas, + 1, + ) + s.Require().NoError(err) + s.Require().NotNil(txResp) + s.Require().True(txResp.CheckTx.Code == 0) + s.Require().True(txResp.DeliverTx.Code != 0) + s.T().Log(txResp.DeliverTx.Log) + s.Require().Contains(txResp.DeliverTx.Log, "insufficient funds") + + // ensure that balance is deducted for any tx passing checkTx + newBalance := s.QueryBalance(s.user3) + s.Require().True(newBalance.IsLT(balance), fmt.Sprintf("new balance: %d, original balance: %d", + balance.Amount.Int64(), + newBalance.Amount.Int64())) + }) + + s.Run("submit a tx with fee greater than full balance - fail checktx", func() { + balance := s.QueryBalance(s.user1) + txResp, err := s.SendCoinsMultiBroadcast( + context.Background(), + s.user1, + s.user3, + sdk.NewCoins(sdk.NewCoin(s.chain.Config().Denom, math.NewInt(sendAmt))), + sdk.NewCoins(balance.AddAmount(math.NewInt(110000))), + gas, + 1, + ) + s.Require().NoError(err) + s.Require().NotNil(txResp) + s.Require().True(txResp.CheckTx.Code != 0) + s.T().Log(txResp.CheckTx.Log) + s.Require().Contains(txResp.CheckTx.Log, "error escrowing funds") + + // ensure that no balance is deducted for a tx failing checkTx + newBalance := s.QueryBalance(s.user1) + s.Require().True(newBalance.Equal(balance), fmt.Sprintf("new balance: %d, original balance: %d", + balance.Amount.Int64(), + newBalance.Amount.Int64())) + }) + + s.Run("submit 2 tx in the same block - fail checktx in 2nd", func() { + balance := s.QueryBalance(s.user2) + + defaultGasPrice := s.QueryDefaultGasPrice() + minBaseFee := sdk.NewDecCoinFromDec(defaultGasPrice.Denom, defaultGasPrice.Amount.Mul(math.LegacyNewDec(gas))) + minBaseFeeCoins := sdk.NewCoins(sdk.NewCoin(minBaseFee.Denom, minBaseFee.Amount.TruncateInt().Add(math. + NewInt(100)))) + // send one tx with no gas or fee attached + txResp, err := s.SendCoinsMultiBroadcastAsync( + context.Background(), + s.user2, + s.user1, + sdk.NewCoins(balance.SubAmount(minBaseFeeCoins.AmountOf(minBaseFee.Denom))), + minBaseFeeCoins, + gas, + 1, + false, + ) + s.Require().NoError(err) + s.Require().NotNil(txResp) + s.Require().True(txResp.Code == 0) + + txResp, err = s.SendCoinsMultiBroadcastAsync( + context.Background(), + s.user2, + s.user1, + minBaseFeeCoins, + minBaseFeeCoins, + gas, + 1, + true, + ) + s.Require().NoError(err) + s.Require().NotNil(txResp) + s.Require().True(txResp.Code == 0) + + nodes := cc.Nodes() + s.Require().True(len(nodes) > 0) + + // wait for 5 blocks + // query height + height, err := s.chain.Height(context.Background()) + s.Require().NoError(err) + s.WaitForHeight(cc, height+5) + + // reset the users and balances + s.user2 = s.GetAndFundTestUsers(context.Background(), s.T().Name(), 200000000000, s.chain) + }) +} diff --git a/testutils/keeper/keeper.go b/testutils/keeper/keeper.go index 9f214b1..ab751a7 100644 --- a/testutils/keeper/keeper.go +++ b/testutils/keeper/keeper.go @@ -46,7 +46,7 @@ func NewTestSetup(t testing.TB, options ...testkeeper.SetupOption) (sdk.Context, require.NoError(t, tk.Initializer.LoadLatest()) // initialize msg servers - feeMarketMsgSrv := feemarketkeeper.NewMsgServer(*feeMarketKeeper) + feeMarketMsgSrv := feemarketkeeper.NewMsgServer(feeMarketKeeper) ctx := sdk.NewContext(tk.Initializer.StateStore, tmproto.Header{ Time: testkeeper.ExampleTimestamp, diff --git a/x/feemarket/ante/expected_keepers.go b/x/feemarket/ante/expected_keepers.go index 0f5120e..20e23f0 100644 --- a/x/feemarket/ante/expected_keepers.go +++ b/x/feemarket/ante/expected_keepers.go @@ -34,6 +34,8 @@ type BankKeeper interface { IsSendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error SendCoins(ctx sdk.Context, from, to sdk.AccAddress, amt sdk.Coins) error SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error + SendCoinsFromModuleToModule(ctx sdk.Context, senderModule, recipientModule string, amt sdk.Coins) error + SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error } // FeeMarketKeeper defines the expected feemarket keeper. diff --git a/x/feemarket/ante/fee.go b/x/feemarket/ante/fee.go index dac2e86..fc1d53a 100644 --- a/x/feemarket/ante/fee.go +++ b/x/feemarket/ante/fee.go @@ -1,23 +1,31 @@ package ante import ( + "bytes" "math" errorsmod "cosmossdk.io/errors" sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" feemarkettypes "github.com/skip-mev/feemarket/x/feemarket/types" ) type feeMarketCheckDecorator struct { feemarketKeeper FeeMarketKeeper + bankKeeper BankKeeper + feegrantKeeper FeeGrantKeeper + accountKeeper AccountKeeper } -func newFeeMarketCheckDecorator(fmk FeeMarketKeeper) feeMarketCheckDecorator { +func newFeeMarketCheckDecorator(ak AccountKeeper, bk BankKeeper, fk FeeGrantKeeper, fmk FeeMarketKeeper) feeMarketCheckDecorator { return feeMarketCheckDecorator{ feemarketKeeper: fmk, + bankKeeper: bk, + feegrantKeeper: fk, + accountKeeper: ak, } } @@ -37,11 +45,11 @@ type FeeMarketCheckDecorator struct { fallbackDecorator sdk.AnteDecorator } -func NewFeeMarketCheckDecorator(fmk FeeMarketKeeper, fallbackDecorator sdk.AnteDecorator) FeeMarketCheckDecorator { +func NewFeeMarketCheckDecorator(ak AccountKeeper, bk BankKeeper, fk FeeGrantKeeper, fmk FeeMarketKeeper, fallbackDecorator sdk.AnteDecorator) FeeMarketCheckDecorator { return FeeMarketCheckDecorator{ feemarketKeeper: fmk, feemarketDecorator: newFeeMarketCheckDecorator( - fmk, + ak, bk, fk, fmk, ), fallbackDecorator: fallbackDecorator, } @@ -131,6 +139,12 @@ func (dfd feeMarketCheckDecorator) anteHandle(ctx sdk.Context, tx sdk.Tx, simula } } + // escrow the entire amount that the account provided as fee (feeCoin) + err = dfd.EscrowFunds(ctx, tx, feeCoin) + if err != nil { + return ctx, errorsmod.Wrapf(err, "error escrowing funds") + } + priorityFee, err := dfd.resolveTxPriorityCoins(ctx, feeCoin, params.FeeDenom) if err != nil { return ctx, errorsmod.Wrapf(err, "error resolving fee priority") @@ -162,6 +176,54 @@ func (dfd feeMarketCheckDecorator) resolveTxPriorityCoins(ctx sdk.Context, fee s return sdk.NewCoin(baseDenom, convertedDec.Amount.TruncateInt()), nil } +// EscrowFunds escrows the fully provided fee from the payer account during tx execution. +// The actual fee is deducted in the post handler along with the tip. +func (dfd feeMarketCheckDecorator) EscrowFunds(ctx sdk.Context, sdkTx sdk.Tx, providedFee sdk.Coin) error { + feeTx, ok := sdkTx.(sdk.FeeTx) + if !ok { + return errorsmod.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx") + } + + feePayer := feeTx.FeePayer() + feeGranter := feeTx.FeeGranter() + deductFeesFrom := feePayer + + // if feegranter set deduct fee from feegranter account. + // this works with only when feegrant enabled. + if feeGranter != nil { + if dfd.feegrantKeeper == nil { + return sdkerrors.ErrInvalidRequest.Wrap("fee grants are not enabled") + } else if !bytes.Equal(feeGranter, feePayer) { + if !providedFee.IsNil() { + err := dfd.feegrantKeeper.UseGrantedFees(ctx, feeGranter, feePayer, sdk.NewCoins(providedFee), sdkTx.GetMsgs()) + if err != nil { + return errorsmod.Wrapf(err, "%s does not allow to pay fees for %s", feeGranter, feePayer) + } + } + } + + deductFeesFrom = feeGranter + } + + deductFeesFromAcc := dfd.accountKeeper.GetAccount(ctx, deductFeesFrom) + if deductFeesFromAcc == nil { + return sdkerrors.ErrUnknownAddress.Wrapf("fee payer address: %s does not exist", deductFeesFrom) + } + + return escrow(dfd.bankKeeper, ctx, deductFeesFromAcc, sdk.NewCoins(providedFee)) +} + +// escrow deducts coins to the escrow. +func escrow(bankKeeper BankKeeper, ctx sdk.Context, acc authtypes.AccountI, coins sdk.Coins) error { + targetModuleAcc := feemarkettypes.FeeCollectorName + err := bankKeeper.SendCoinsFromAccountToModule(ctx, acc.GetAddress(), targetModuleAcc, coins) + if err != nil { + return err + } + + return nil +} + // CheckTxFee implements the logic for the fee market to check if a Tx has provided sufficient // fees given the current state of the fee market. Returns an error if insufficient fees. func CheckTxFee(ctx sdk.Context, gasPrice sdk.DecCoin, feeCoin sdk.Coin, feeGas int64, isAnte bool) (payCoin sdk.Coin, tip sdk.Coin, err error) { diff --git a/x/feemarket/ante/fee_test.go b/x/feemarket/ante/fee_test.go index 69e36ab..8955492 100644 --- a/x/feemarket/ante/fee_test.go +++ b/x/feemarket/ante/fee_test.go @@ -4,6 +4,8 @@ import ( "fmt" "testing" + "github.com/stretchr/testify/mock" + "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" @@ -25,8 +27,8 @@ func TestAnteHandle(t *testing.T) { testCases := []antesuite.TestCase{ { Name: "0 gas given should fail", - Malleate: func(suite *antesuite.TestSuite) antesuite.TestCaseArgs { - accs := suite.CreateTestAccounts(1) + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) return antesuite.TestCaseArgs{ Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, @@ -44,9 +46,10 @@ func TestAnteHandle(t *testing.T) { // when --gas=auto is set, cosmos-sdk sets gas=0 and simulate=true { Name: "--gas=auto behaviour test", - Malleate: func(suite *antesuite.TestSuite) antesuite.TestCaseArgs { - accs := suite.CreateTestAccounts(1) - + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[0].Account.GetAddress(), + types.FeeCollectorName, mock.Anything).Return(nil) return antesuite.TestCaseArgs{ Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, GasLimit: 0, @@ -60,8 +63,8 @@ func TestAnteHandle(t *testing.T) { }, { Name: "0 gas given should fail with resolvable denom", - Malleate: func(suite *antesuite.TestSuite) antesuite.TestCaseArgs { - accs := suite.CreateTestAccounts(1) + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) return antesuite.TestCaseArgs{ Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, @@ -77,9 +80,10 @@ func TestAnteHandle(t *testing.T) { }, { Name: "0 gas given should pass in simulate - no fee", - Malleate: func(suite *antesuite.TestSuite) antesuite.TestCaseArgs { - accs := suite.CreateTestAccounts(1) - + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[0].Account.GetAddress(), + types.FeeCollectorName, mock.Anything).Return(nil).Once() return antesuite.TestCaseArgs{ Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, GasLimit: 0, @@ -94,9 +98,10 @@ func TestAnteHandle(t *testing.T) { }, { Name: "0 gas given should pass in simulate - fee", - Malleate: func(suite *antesuite.TestSuite) antesuite.TestCaseArgs { - accs := suite.CreateTestAccounts(1) - + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[0].Account.GetAddress(), + types.FeeCollectorName, mock.Anything).Return(nil).Once() return antesuite.TestCaseArgs{ Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, GasLimit: 0, @@ -111,8 +116,10 @@ func TestAnteHandle(t *testing.T) { }, { Name: "signer has enough funds, should pass", - Malleate: func(suite *antesuite.TestSuite) antesuite.TestCaseArgs { - accs := suite.CreateTestAccounts(1) + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[0].Account.GetAddress(), + types.FeeCollectorName, mock.Anything).Return(nil) return antesuite.TestCaseArgs{ Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, GasLimit: gasLimit, @@ -127,8 +134,10 @@ func TestAnteHandle(t *testing.T) { }, { Name: "signer has enough funds in resolvable denom, should pass", - Malleate: func(suite *antesuite.TestSuite) antesuite.TestCaseArgs { - accs := suite.CreateTestAccounts(1) + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[0].Account.GetAddress(), + types.FeeCollectorName, mock.Anything).Return(nil).Once() return antesuite.TestCaseArgs{ Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, GasLimit: gasLimit, @@ -173,7 +182,7 @@ func TestAnteHandle(t *testing.T) { RunPost: true, Simulate: false, ExpPass: false, - ExpErr: sdkerrors.ErrInvalidGasLimit, + ExpErr: sdkerrors.ErrOutOfGas, }, } diff --git a/x/feemarket/post/feegrant_test.go b/x/feemarket/ante/feegrant_test.go similarity index 87% rename from x/feemarket/post/feegrant_test.go rename to x/feemarket/ante/feegrant_test.go index 97587bf..588cf5f 100644 --- a/x/feemarket/post/feegrant_test.go +++ b/x/feemarket/ante/feegrant_test.go @@ -1,35 +1,32 @@ -package post_test +package ante_test import ( "math/rand" "testing" "time" - "github.com/stretchr/testify/mock" - - "github.com/skip-mev/feemarket/x/feemarket/types" - - "github.com/stretchr/testify/require" - "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/testutil/testdata" - sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/cosmos/cosmos-sdk/types/tx/signing" + authante "github.com/cosmos/cosmos-sdk/x/auth/ante" authsign "github.com/cosmos/cosmos-sdk/x/auth/signing" "github.com/cosmos/cosmos-sdk/x/auth/tx" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/feegrant" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + feemarketante "github.com/skip-mev/feemarket/x/feemarket/ante" antesuite "github.com/skip-mev/feemarket/x/feemarket/ante/suite" - feemarketpost "github.com/skip-mev/feemarket/x/feemarket/post" + "github.com/skip-mev/feemarket/x/feemarket/types" ) -func TestDeductFeesNoDelegation(t *testing.T) { +func TestEscrowFunds(t *testing.T) { cases := map[string]struct { fee int64 valid bool @@ -50,8 +47,8 @@ func TestDeductFeesNoDelegation(t *testing.T) { valid: true, malleate: func(s *antesuite.TestSuite) (antesuite.TestAccount, sdk.AccAddress) { accs := s.CreateTestAccounts(1) - s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[0].Account.GetAddress(), types.FeeCollectorName, mock.Anything).Return(nil).Once() - s.MockBankKeeper.On("SendCoins", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() + s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[0].Account.GetAddress(), + types.FeeCollectorName, mock.Anything).Return(nil) return accs[0], nil }, @@ -78,8 +75,8 @@ func TestDeductFeesNoDelegation(t *testing.T) { malleate: func(s *antesuite.TestSuite) (antesuite.TestAccount, sdk.AccAddress) { accs := s.CreateTestAccounts(2) s.MockFeeGrantKeeper.On("UseGrantedFees", mock.Anything, accs[1].Account.GetAddress(), accs[0].Account.GetAddress(), mock.Anything, mock.Anything).Return(nil).Once() - s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[1].Account.GetAddress(), types.FeeCollectorName, mock.Anything).Return(nil).Once() - s.MockBankKeeper.On("SendCoins", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() + s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[1].Account.GetAddress(), + types.FeeCollectorName, mock.Anything).Return(nil) return accs[0], accs[1].Account.GetAddress() }, @@ -117,7 +114,8 @@ func TestDeductFeesNoDelegation(t *testing.T) { malleate: func(s *antesuite.TestSuite) (antesuite.TestAccount, sdk.AccAddress) { accs := s.CreateTestAccounts(2) s.MockFeeGrantKeeper.On("UseGrantedFees", mock.Anything, accs[1].Account.GetAddress(), accs[0].Account.GetAddress(), mock.Anything, mock.Anything).Return(nil).Once() - s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[1].Account.GetAddress(), types.FeeCollectorName, mock.Anything).Return(sdkerrors.ErrInsufficientFunds).Once() + s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[1].Account.GetAddress(), + types.FeeCollectorName, mock.Anything).Return(sdkerrors.ErrInsufficientFunds) return accs[0], accs[1].Account.GetAddress() }, }, @@ -129,8 +127,14 @@ func TestDeductFeesNoDelegation(t *testing.T) { s := antesuite.SetupTestSuite(t, true) protoTxCfg := tx.NewTxConfig(codec.NewProtoCodec(s.EncCfg.InterfaceRegistry), tx.DefaultSignModes) // this just tests our handler - dfd := feemarketpost.NewFeeMarketDeductDecorator(s.AccountKeeper, s.MockBankKeeper, s.MockFeeGrantKeeper, s.FeeMarketKeeper) - feePostHandler := sdk.ChainPostDecorators(dfd) + dfd := feemarketante.NewFeeMarketCheckDecorator(s.AccountKeeper, s.MockBankKeeper, s.MockFeeGrantKeeper, + s.FeeMarketKeeper, authante.NewDeductFeeDecorator( + s.AccountKeeper, + s.MockBankKeeper, + s.MockFeeGrantKeeper, + nil, + )) + feeAnteHandler := sdk.ChainAnteDecorators(dfd) signer, feeAcc := stc.malleate(s) @@ -146,7 +150,7 @@ func TestDeductFeesNoDelegation(t *testing.T) { var defaultGenTxGas uint64 = 10 tx, err := genTxWithFeeGranter(protoTxCfg, msgs, fee, defaultGenTxGas, s.Ctx.ChainID(), accNums, seqs, feeAcc, privs...) require.NoError(t, err) - _, err = feePostHandler(s.Ctx, tx, false, true) // tests only feegrant post + _, err = feeAnteHandler(s.Ctx, tx, false) // tests only feegrant ante if tc.valid { require.NoError(t, err) } else { diff --git a/x/feemarket/ante/mocks/mock_bank_keeper.go b/x/feemarket/ante/mocks/mock_bank_keeper.go index f421be0..db4fc50 100644 --- a/x/feemarket/ante/mocks/mock_bank_keeper.go +++ b/x/feemarket/ante/mocks/mock_bank_keeper.go @@ -73,6 +73,42 @@ func (_m *BankKeeper) SendCoinsFromAccountToModule(ctx types.Context, senderAddr return r0 } +// SendCoinsFromModuleToAccount provides a mock function with given fields: ctx, senderModule, recipientAddr, amt +func (_m *BankKeeper) SendCoinsFromModuleToAccount(ctx types.Context, senderModule string, recipientAddr types.AccAddress, amt types.Coins) error { + ret := _m.Called(ctx, senderModule, recipientAddr, amt) + + if len(ret) == 0 { + panic("no return value specified for SendCoinsFromModuleToAccount") + } + + var r0 error + if rf, ok := ret.Get(0).(func(types.Context, string, types.AccAddress, types.Coins) error); ok { + r0 = rf(ctx, senderModule, recipientAddr, amt) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// SendCoinsFromModuleToModule provides a mock function with given fields: ctx, senderModule, recipientModule, amt +func (_m *BankKeeper) SendCoinsFromModuleToModule(ctx types.Context, senderModule string, recipientModule string, amt types.Coins) error { + ret := _m.Called(ctx, senderModule, recipientModule, amt) + + if len(ret) == 0 { + panic("no return value specified for SendCoinsFromModuleToModule") + } + + var r0 error + if rf, ok := ret.Get(0).(func(types.Context, string, string, types.Coins) error); ok { + r0 = rf(ctx, senderModule, recipientModule, amt) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // NewBankKeeper creates a new instance of BankKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewBankKeeper(t interface { diff --git a/x/feemarket/ante/suite/suite.go b/x/feemarket/ante/suite/suite.go index f9157ef..7ea2441 100644 --- a/x/feemarket/ante/suite/suite.go +++ b/x/feemarket/ante/suite/suite.go @@ -5,22 +5,27 @@ import ( "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" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/std" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/tx/signing" authante "github.com/cosmos/cosmos-sdk/x/auth/ante" authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - "github.com/skip-mev/chaintestutil/encoding" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" - "github.com/skip-mev/feemarket/tests/app" testkeeper "github.com/skip-mev/feemarket/testutils/keeper" feemarketante "github.com/skip-mev/feemarket/x/feemarket/ante" "github.com/skip-mev/feemarket/x/feemarket/ante/mocks" + feemarketkeeper "github.com/skip-mev/feemarket/x/feemarket/keeper" feemarketpost "github.com/skip-mev/feemarket/x/feemarket/post" + feemarkettypes "github.com/skip-mev/feemarket/x/feemarket/types" ) type TestSuite struct { @@ -33,13 +38,16 @@ type TestSuite struct { TxBuilder client.TxBuilder AccountKeeper feemarketante.AccountKeeper - FeeMarketKeeper feemarketpost.FeeMarketKeeper + FeeMarketKeeper *feemarketkeeper.Keeper BankKeeper feemarketante.BankKeeper FeeGrantKeeper feemarketante.FeeGrantKeeper MockBankKeeper *mocks.BankKeeper MockFeeGrantKeeper *mocks.FeeGrantKeeper - EncCfg encoding.TestEncodingConfig + + EncCfg TestEncodingConfig + + MsgServer feemarkettypes.MsgServer } // TestAccount represents an account used in the tests in x/auth/ante. @@ -69,7 +77,7 @@ func (s *TestSuite) CreateTestAccounts(numAccs int) []TestAccount { func SetupTestSuite(t *testing.T, mock bool) *TestSuite { s := &TestSuite{} - s.EncCfg = encoding.MakeTestEncodingConfig(app.ModuleBasics.RegisterInterfaces) + s.EncCfg = MakeTestEncodingConfig() ctx, testKeepers, _ := testkeeper.NewTestSetup(t) s.Ctx = ctx @@ -81,6 +89,9 @@ func SetupTestSuite(t *testing.T, mock bool) *TestSuite { s.ClientCtx = client.Context{}.WithTxConfig(s.EncCfg.TxConfig) s.TxBuilder = s.ClientCtx.TxConfig.NewTxBuilder() + s.FeeMarketKeeper.SetEnabledHeight(s.Ctx, -1) + s.MsgServer = feemarketkeeper.NewMsgServer(s.FeeMarketKeeper) + s.SetupHandlers(mock) s.SetT(t) return s @@ -89,6 +100,7 @@ func SetupTestSuite(t *testing.T, mock bool) *TestSuite { func (s *TestSuite) SetupHandlers(mock bool) { bankKeeper := s.BankKeeper feeGrantKeeper := s.FeeGrantKeeper + if mock { bankKeeper = s.MockBankKeeper feeGrantKeeper = s.MockFeeGrantKeeper @@ -98,11 +110,14 @@ func (s *TestSuite) SetupHandlers(mock bool) { anteDecorators := []sdk.AnteDecorator{ authante.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first feemarketante.NewFeeMarketCheckDecorator( // fee market replaces fee deduct decorator + s.AccountKeeper, + bankKeeper, + feeGrantKeeper, s.FeeMarketKeeper, authante.NewDeductFeeDecorator( s.AccountKeeper, - s.BankKeeper, - s.FeeGrantKeeper, + bankKeeper, + feeGrantKeeper, nil, ), ), @@ -116,7 +131,6 @@ func (s *TestSuite) SetupHandlers(mock bool) { feemarketpost.NewFeeMarketDeductDecorator( s.AccountKeeper, bankKeeper, - feeGrantKeeper, s.FeeMarketKeeper, ), } @@ -128,6 +142,7 @@ func (s *TestSuite) SetupHandlers(mock bool) { type TestCase struct { Name string Malleate func(*TestSuite) TestCaseArgs + StateUpdate func(*TestSuite) RunAnte bool RunPost bool Simulate bool @@ -169,21 +184,28 @@ func (s *TestSuite) RunTestCase(t *testing.T, tc TestCase, args TestCaseArgs) { tx, txErr := s.CreateTestTx(args.Privs, args.AccNums, args.AccSeqs, args.ChainID) var ( - newCtx sdk.Context - handleErr error + newCtx sdk.Context + anteErr error + postErr error ) if tc.RunAnte { - newCtx, handleErr = s.AnteHandler(s.Ctx, tx, tc.Simulate) + newCtx, anteErr = s.AnteHandler(s.Ctx, tx, tc.Simulate) + } + + // perform mid-tx state update if configured + if tc.StateUpdate != nil { + tc.StateUpdate(s) } - if tc.RunPost { - newCtx, handleErr = s.PostHandler(s.Ctx, tx, tc.Simulate, true) + if tc.RunPost && anteErr == nil { + newCtx, postErr = s.PostHandler(s.Ctx, tx, tc.Simulate, true) } if tc.ExpPass { require.NoError(t, txErr) - require.NoError(t, handleErr) + require.NoError(t, anteErr) + require.NoError(t, postErr) require.NotNil(t, newCtx) s.Ctx = newCtx @@ -198,9 +220,15 @@ func (s *TestSuite) RunTestCase(t *testing.T, tc TestCase, args TestCaseArgs) { require.Error(t, txErr) require.ErrorIs(t, txErr, tc.ExpErr) - case handleErr != nil: - require.Error(t, handleErr) - require.ErrorIs(t, handleErr, tc.ExpErr) + case anteErr != nil: + require.Error(t, anteErr) + require.NoError(t, postErr) + require.ErrorIs(t, anteErr, tc.ExpErr) + + case postErr != nil: + require.NoError(t, anteErr) + require.Error(t, postErr) + require.ErrorIs(t, postErr, tc.ExpErr) default: t.Fatal("expected one of txErr, handleErr to be an error") @@ -264,3 +292,44 @@ func NewTestFeeAmount() sdk.Coins { func NewTestGasLimit() uint64 { return 200000 } + +// TestEncodingConfig specifies the concrete encoding types to use for a given app. +// This is provided for compatibility between protobuf and amino implementations. +type TestEncodingConfig struct { + InterfaceRegistry codectypes.InterfaceRegistry + Codec codec.Codec + TxConfig client.TxConfig + Amino *codec.LegacyAmino +} + +// MakeTestEncodingConfig creates a test EncodingConfig for a test configuration. +func MakeTestEncodingConfig() TestEncodingConfig { + amino := codec.NewLegacyAmino() + + interfaceRegistry := InterfaceRegistry() + cdc := codec.NewProtoCodec(interfaceRegistry) + txCfg := authtx.NewTxConfig(cdc, authtx.DefaultSignModes) + + std.RegisterLegacyAminoCodec(amino) + std.RegisterInterfaces(interfaceRegistry) + + return TestEncodingConfig{ + InterfaceRegistry: interfaceRegistry, + Codec: cdc, + TxConfig: txCfg, + Amino: amino, + } +} + +func InterfaceRegistry() codectypes.InterfaceRegistry { + interfaceRegistry := codectypes.NewInterfaceRegistry() + + // always register + cryptocodec.RegisterInterfaces(interfaceRegistry) + authtypes.RegisterInterfaces(interfaceRegistry) + + // call extra registry functions + feemarkettypes.RegisterInterfaces(interfaceRegistry) + + return interfaceRegistry +} diff --git a/x/feemarket/keeper/genesis.go b/x/feemarket/keeper/genesis.go index 5226c82..a576984 100644 --- a/x/feemarket/keeper/genesis.go +++ b/x/feemarket/keeper/genesis.go @@ -24,6 +24,9 @@ func (k *Keeper) InitGenesis(ctx sdk.Context, gs types.GenesisState) { if err := k.SetState(ctx, gs.State); err != nil { panic(err) } + + // always init enabled height to -1 until it is explicitly set later in the application + k.SetEnabledHeight(ctx, -1) } // ExportGenesis returns a GenesisState for a given context. diff --git a/x/feemarket/keeper/keeper.go b/x/feemarket/keeper/keeper.go index f207fda..39ee40f 100644 --- a/x/feemarket/keeper/keeper.go +++ b/x/feemarket/keeper/keeper.go @@ -2,6 +2,7 @@ package keeper import ( "fmt" + "strconv" "github.com/cometbft/cometbft/libs/log" "github.com/cosmos/cosmos-sdk/codec" @@ -56,6 +57,28 @@ func (k *Keeper) GetAuthority() string { return k.authority } +// GetEnabledHeight returns the height at which the feemarket was enabled. +func (k *Keeper) GetEnabledHeight(ctx sdk.Context) (int64, error) { + store := ctx.KVStore(k.storeKey) + + key := types.KeyEnabledHeight + bz := store.Get(key) + if bz == nil { + return -1, nil + } + + return strconv.ParseInt(string(bz), 10, 64) +} + +// SetEnabledHeight sets the height at which the feemarket was enabled. +func (k *Keeper) SetEnabledHeight(ctx sdk.Context, height int64) { + store := ctx.KVStore(k.storeKey) + + bz := []byte(strconv.FormatInt(height, 10)) + + store.Set(types.KeyEnabledHeight, bz) +} + // ResolveToDenom converts the given coin to the given denomination. func (k *Keeper) ResolveToDenom(ctx sdk.Context, coin sdk.DecCoin, denom string) (sdk.DecCoin, error) { if k.resolver == nil { diff --git a/x/feemarket/keeper/keeper_test.go b/x/feemarket/keeper/keeper_test.go index 10e8e9f..e633a64 100644 --- a/x/feemarket/keeper/keeper_test.go +++ b/x/feemarket/keeper/keeper_test.go @@ -4,7 +4,13 @@ import ( "testing" "cosmossdk.io/math" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/std" sdk "github.com/cosmos/cosmos-sdk/types" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/skip-mev/chaintestutil/encoding" @@ -47,6 +53,7 @@ func (s *KeeperTestSuite) SetupTest() { s.feeMarketKeeper = tk.FeeMarketKeeper s.msgServer = tm.FeeMarketMsgServer s.queryServer = keeper.NewQueryServer(*s.feeMarketKeeper) + s.feeMarketKeeper.SetEnabledHeight(s.ctx, -1) } func (s *KeeperTestSuite) TestState() { @@ -111,3 +118,54 @@ func (s *KeeperTestSuite) TestParams() { s.Require().EqualValues(params, gotParams) }) } + +func (s *KeeperTestSuite) TestEnabledHeight() { + s.Run("get and set values", func() { + s.feeMarketKeeper.SetEnabledHeight(s.ctx, 10) + + got, err := s.feeMarketKeeper.GetEnabledHeight(s.ctx) + s.Require().NoError(err) + s.Require().Equal(int64(10), got) + }) +} + +// TestEncodingConfig specifies the concrete encoding types to use for a given app. +// This is provided for compatibility between protobuf and amino implementations. +type TestEncodingConfig struct { + InterfaceRegistry codectypes.InterfaceRegistry + Codec codec.Codec + TxConfig client.TxConfig + Amino *codec.LegacyAmino +} + +// MakeTestEncodingConfig creates a test EncodingConfig for a test configuration. +func MakeTestEncodingConfig() TestEncodingConfig { + amino := codec.NewLegacyAmino() + + interfaceRegistry := InterfaceRegistry() + cdc := codec.NewProtoCodec(interfaceRegistry) + txCfg := authtx.NewTxConfig(cdc, authtx.DefaultSignModes) + + std.RegisterLegacyAminoCodec(amino) + std.RegisterInterfaces(interfaceRegistry) + + return TestEncodingConfig{ + InterfaceRegistry: interfaceRegistry, + Codec: cdc, + TxConfig: txCfg, + Amino: amino, + } +} + +func InterfaceRegistry() codectypes.InterfaceRegistry { + interfaceRegistry := codectypes.NewInterfaceRegistry() + + // always register + cryptocodec.RegisterInterfaces(interfaceRegistry) + authtypes.RegisterInterfaces(interfaceRegistry) + + // call extra registry functions + types.RegisterInterfaces(interfaceRegistry) + + return interfaceRegistry +} diff --git a/x/feemarket/keeper/msg_server.go b/x/feemarket/keeper/msg_server.go index f1ce181..b4d35da 100644 --- a/x/feemarket/keeper/msg_server.go +++ b/x/feemarket/keeper/msg_server.go @@ -13,11 +13,11 @@ var _ types.MsgServer = (*MsgServer)(nil) // MsgServer is the server API for x/feemarket Msg service. type MsgServer struct { - k Keeper + k *Keeper } // NewMsgServer returns the MsgServer implementation. -func NewMsgServer(k Keeper) types.MsgServer { +func NewMsgServer(k *Keeper) types.MsgServer { return &MsgServer{k} } @@ -30,6 +30,16 @@ func (ms MsgServer) Params(goCtx context.Context, msg *types.MsgParams) (*types. return nil, fmt.Errorf("invalid authority to execute message") } + gotParams, err := ms.k.GetParams(ctx) + if err != nil { + return nil, fmt.Errorf("error getting params: %w", err) + } + + // if going from disabled -> enabled, set enabled height + if !gotParams.Enabled && msg.Params.Enabled { + ms.k.SetEnabledHeight(ctx, ctx.BlockHeight()) + } + params := msg.Params if err := ms.k.SetParams(ctx, params); err != nil { return nil, fmt.Errorf("error setting params: %w", err) diff --git a/x/feemarket/keeper/msg_server_test.go b/x/feemarket/keeper/msg_server_test.go index 57cbd1e..b3f3f89 100644 --- a/x/feemarket/keeper/msg_server_test.go +++ b/x/feemarket/keeper/msg_server_test.go @@ -40,6 +40,46 @@ func (s *KeeperTestSuite) TestMsgParams() { s.Require().Error(err) }) + s.Run("sets enabledHeight when transitioning from disabled -> enabled", func() { + s.ctx = s.ctx.WithBlockHeight(s.ctx.BlockHeight()) + enabledParams := types.DefaultParams() + + req := &types.MsgParams{ + Authority: s.authorityAccount.String(), + Params: enabledParams, + } + _, err := s.msgServer.Params(s.ctx, req) + s.Require().NoError(err) + + disableParams := types.DefaultParams() + disableParams.Enabled = false + + req = &types.MsgParams{ + Authority: s.authorityAccount.String(), + Params: disableParams, + } + _, err = s.msgServer.Params(s.ctx, req) + s.Require().NoError(err) + + gotHeight, err := s.feeMarketKeeper.GetEnabledHeight(s.ctx) + s.Require().NoError(err) + s.Require().Equal(s.ctx.BlockHeight(), gotHeight) + + // now that the markets are disabled, enable and check block height + s.ctx = s.ctx.WithBlockHeight(s.ctx.BlockHeight() + 10) + + req = &types.MsgParams{ + Authority: s.authorityAccount.String(), + Params: enabledParams, + } + _, err = s.msgServer.Params(s.ctx, req) + s.Require().NoError(err) + + newHeight, err := s.feeMarketKeeper.GetEnabledHeight(s.ctx) + s.Require().NoError(err) + s.Require().Equal(s.ctx.BlockHeight(), newHeight) + }) + s.Run("resets state after new params request", func() { params, err := s.feeMarketKeeper.GetParams(s.ctx) s.Require().NoError(err) diff --git a/x/feemarket/module.go b/x/feemarket/module.go index 7bbd8ca..94be5a9 100644 --- a/x/feemarket/module.go +++ b/x/feemarket/module.go @@ -110,7 +110,7 @@ func (AppModule) ConsensusVersion() uint64 { return ConsensusVersion } // RegisterServices registers the module's services with the app's module configurator. func (am AppModule) RegisterServices(cfc module.Configurator) { - types.RegisterMsgServer(cfc.MsgServer(), keeper.NewMsgServer(am.k)) + types.RegisterMsgServer(cfc.MsgServer(), keeper.NewMsgServer(&am.k)) types.RegisterQueryServer(cfc.QueryServer(), keeper.NewQueryServer(am.k)) } diff --git a/x/feemarket/post/expected_keeper.go b/x/feemarket/post/expected_keeper.go index 2ade26f..9b5a7cc 100644 --- a/x/feemarket/post/expected_keeper.go +++ b/x/feemarket/post/expected_keeper.go @@ -20,13 +20,6 @@ type AccountKeeper interface { NewAccountWithAddress(ctx sdk.Context, addr sdk.AccAddress) authtypes.AccountI } -// FeeGrantKeeper defines the expected feegrant keeper. -// -//go:generate mockery --name FeeGrantKeeper --filename mock_feegrant_keeper.go -type FeeGrantKeeper interface { - UseGrantedFees(ctx sdk.Context, granter, grantee sdk.AccAddress, fee sdk.Coins, msgs []sdk.Msg) error -} - // BankKeeper defines the contract needed for supply related APIs. // //go:generate mockery --name BankKeeper --filename mock_bank_keeper.go @@ -34,6 +27,8 @@ type BankKeeper interface { IsSendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error SendCoins(ctx sdk.Context, from, to sdk.AccAddress, amt sdk.Coins) error SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error + SendCoinsFromModuleToModule(ctx sdk.Context, senderModule, recipientModule string, amt sdk.Coins) error + SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error } // FeeMarketKeeper defines the expected feemarket keeper. @@ -46,4 +41,5 @@ type FeeMarketKeeper interface { SetState(ctx sdk.Context, state feemarkettypes.State) error ResolveToDenom(ctx sdk.Context, coin sdk.DecCoin, denom string) (sdk.DecCoin, error) GetMinGasPrice(ctx sdk.Context, denom string) (sdk.DecCoin, error) + GetEnabledHeight(ctx sdk.Context) (int64, error) } diff --git a/x/feemarket/post/fee.go b/x/feemarket/post/fee.go index 923d8fd..0a385a3 100644 --- a/x/feemarket/post/fee.go +++ b/x/feemarket/post/fee.go @@ -23,15 +23,13 @@ import ( type FeeMarketDeductDecorator struct { accountKeeper AccountKeeper bankKeeper BankKeeper - feegrantKeeper FeeGrantKeeper feemarketKeeper FeeMarketKeeper } -func NewFeeMarketDeductDecorator(ak AccountKeeper, bk BankKeeper, fk FeeGrantKeeper, fmk FeeMarketKeeper) FeeMarketDeductDecorator { +func NewFeeMarketDeductDecorator(ak AccountKeeper, bk BankKeeper, fmk FeeMarketKeeper) FeeMarketDeductDecorator { return FeeMarketDeductDecorator{ accountKeeper: ak, bankKeeper: bk, - feegrantKeeper: fk, feemarketKeeper: fmk, } } @@ -65,6 +63,16 @@ func (dfd FeeMarketDeductDecorator) PostHandle(ctx sdk.Context, tx sdk.Tx, simul return next(ctx, tx, simulate, success) } + enabledHeight, err := dfd.feemarketKeeper.GetEnabledHeight(ctx) + if err != nil { + return ctx, errorsmod.Wrapf(err, "unable to get fee market enabled height") + } + + // if the current height is that which enabled the feemarket or lower, skip deduction + if ctx.BlockHeight() <= enabledHeight { + return next(ctx, tx, simulate, success) + } + // update fee market state state, err := dfd.feemarketKeeper.GetState(ctx) if err != nil { @@ -118,7 +126,7 @@ func (dfd FeeMarketDeductDecorator) PostHandle(ctx sdk.Context, tx sdk.Tx, simul "tip", tip, ) - if err := dfd.DeductFeeAndTip(ctx, tx, payCoin, tip); err != nil { + if err := dfd.PayOutFeeAndTip(ctx, payCoin, tip); err != nil { return ctx, err } @@ -135,59 +143,19 @@ func (dfd FeeMarketDeductDecorator) PostHandle(ctx sdk.Context, tx sdk.Tx, simul return next(ctx, tx, simulate, success) } -// DeductFeeAndTip deducts the provided fee and tip from the fee payer. +// PayOutFeeAndTip deducts the provided fee and tip from the fee payer. // If the tx uses a feegranter, the fee granter address will pay the fee instead of the tx signer. -func (dfd FeeMarketDeductDecorator) DeductFeeAndTip(ctx sdk.Context, sdkTx sdk.Tx, fee, tip sdk.Coin) error { - feeTx, ok := sdkTx.(sdk.FeeTx) - if !ok { - return errorsmod.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx") - } - - if addr := dfd.accountKeeper.GetModuleAddress(feemarkettypes.FeeCollectorName); addr == nil { - return fmt.Errorf("fee collector module account (%s) has not been set", feemarkettypes.FeeCollectorName) - } - - if addr := dfd.accountKeeper.GetModuleAddress(authtypes.FeeCollectorName); addr == nil { - return fmt.Errorf("default fee collector module account (%s) has not been set", authtypes.FeeCollectorName) - } - +func (dfd FeeMarketDeductDecorator) PayOutFeeAndTip(ctx sdk.Context, fee, tip sdk.Coin) error { params, err := dfd.feemarketKeeper.GetParams(ctx) if err != nil { return fmt.Errorf("error getting feemarket params: %v", err) } - feePayer := feeTx.FeePayer() - feeGranter := feeTx.FeeGranter() - deductFeesFrom := feePayer - distributeFees := params.DistributeFees - - // if feegranter set deduct fee from feegranter account. - // this works with only when feegrant enabled. - if feeGranter != nil { - if dfd.feegrantKeeper == nil { - return sdkerrors.ErrInvalidRequest.Wrap("fee grants are not enabled") - } else if !feeGranter.Equals(feePayer) { - if !fee.IsNil() { - err := dfd.feegrantKeeper.UseGrantedFees(ctx, feeGranter, feePayer, sdk.NewCoins(fee), sdkTx.GetMsgs()) - if err != nil { - return errorsmod.Wrapf(err, "%s does not allow to pay fees for %s", feeGranter, feePayer) - } - } - } - - deductFeesFrom = feeGranter - } - - deductFeesFromAcc := dfd.accountKeeper.GetAccount(ctx, deductFeesFrom) - if deductFeesFromAcc == nil { - return sdkerrors.ErrUnknownAddress.Wrapf("fee payer address: %s does not exist", deductFeesFrom) - } - var events sdk.Events // deduct the fees and tip if !fee.IsNil() { - err := DeductCoins(dfd.bankKeeper, ctx, deductFeesFromAcc, sdk.NewCoins(fee), distributeFees) + err := DeductCoins(dfd.bankKeeper, ctx, sdk.NewCoins(fee), params.DistributeFees) if err != nil { return err } @@ -195,13 +163,12 @@ func (dfd FeeMarketDeductDecorator) DeductFeeAndTip(ctx sdk.Context, sdkTx sdk.T events = append(events, sdk.NewEvent( feemarkettypes.EventTypeFeePay, sdk.NewAttribute(sdk.AttributeKeyFee, fee.String()), - sdk.NewAttribute(sdk.AttributeKeyFeePayer, deductFeesFrom.String()), )) } proposer := sdk.AccAddress(ctx.BlockHeader().ProposerAddress) if !tip.IsNil() { - err := SendTip(dfd.bankKeeper, ctx, deductFeesFromAcc.GetAddress(), proposer, sdk.NewCoins(tip)) + err := SendTip(dfd.bankKeeper, ctx, proposer, sdk.NewCoins(tip)) if err != nil { return err } @@ -209,7 +176,6 @@ func (dfd FeeMarketDeductDecorator) DeductFeeAndTip(ctx sdk.Context, sdkTx sdk.T events = append(events, sdk.NewEvent( feemarkettypes.EventTypeTipPay, sdk.NewAttribute(feemarkettypes.AttributeKeyTip, tip.String()), - sdk.NewAttribute(feemarkettypes.AttributeKeyTipPayer, deductFeesFrom.String()), sdk.NewAttribute(feemarkettypes.AttributeKeyTipPayee, proposer.String()), )) } @@ -219,24 +185,21 @@ func (dfd FeeMarketDeductDecorator) DeductFeeAndTip(ctx sdk.Context, sdkTx sdk.T } // DeductCoins deducts coins from the given account. -// Coins can be sent to the default fee collector (causes coins to be distributed to stakers) or sent to the feemarket fee collector account (causes coins to be burned). -func DeductCoins(bankKeeper BankKeeper, ctx sdk.Context, acc authtypes.AccountI, coins sdk.Coins, distributeFees bool) error { - targetModuleAcc := feemarkettypes.FeeCollectorName +// Coins can be sent to the default fee collector ( +// causes coins to be distributed to stakers) or kept in the fee collector account (soft burn). +func DeductCoins(bankKeeper BankKeeper, ctx sdk.Context, coins sdk.Coins, distributeFees bool) error { if distributeFees { - targetModuleAcc = authtypes.FeeCollectorName - } - - err := bankKeeper.SendCoinsFromAccountToModule(ctx, acc.GetAddress(), targetModuleAcc, coins) - if err != nil { - return err + err := bankKeeper.SendCoinsFromModuleToModule(ctx, feemarkettypes.FeeCollectorName, authtypes.FeeCollectorName, coins) + if err != nil { + return err + } } - return nil } // SendTip sends a tip to the current block proposer. -func SendTip(bankKeeper BankKeeper, ctx sdk.Context, acc, proposer sdk.AccAddress, coins sdk.Coins) error { - err := bankKeeper.SendCoins(ctx, acc, proposer, coins) +func SendTip(bankKeeper BankKeeper, ctx sdk.Context, proposer sdk.AccAddress, coins sdk.Coins) error { + err := bankKeeper.SendCoinsFromModuleToAccount(ctx, feemarkettypes.FeeCollectorName, proposer, coins) if err != nil { return err } diff --git a/x/feemarket/post/fee_test.go b/x/feemarket/post/fee_test.go index a9f952a..f6001fa 100644 --- a/x/feemarket/post/fee_test.go +++ b/x/feemarket/post/fee_test.go @@ -4,6 +4,8 @@ import ( "fmt" "testing" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" @@ -18,33 +20,58 @@ import ( func TestDeductCoins(t *testing.T) { tests := []struct { - name string - coins sdk.Coins - wantErr bool + name string + coins sdk.Coins + distributeFees bool + wantErr bool }{ { - name: "valid", - coins: sdk.NewCoins(sdk.NewCoin("test", sdk.NewInt(10))), - wantErr: false, + name: "valid", + coins: sdk.NewCoins(sdk.NewCoin("test", math.NewInt(10))), + distributeFees: false, + wantErr: false, }, { - name: "valid no coins", - coins: sdk.NewCoins(), - wantErr: false, + name: "valid no coins", + coins: sdk.NewCoins(), + distributeFees: false, + wantErr: false, }, { - name: "valid zero coin", - coins: sdk.NewCoins(sdk.NewCoin("test", math.ZeroInt())), - wantErr: false, + name: "valid zero coin", + coins: sdk.NewCoins(sdk.NewCoin("test", math.ZeroInt())), + distributeFees: false, + wantErr: false, + }, + { + name: "valid - distribute", + coins: sdk.NewCoins(sdk.NewCoin("test", math.NewInt(10))), + distributeFees: true, + wantErr: false, + }, + { + name: "valid no coins - distribute", + coins: sdk.NewCoins(), + distributeFees: true, + wantErr: false, + }, + { + name: "valid zero coin - distribute", + coins: sdk.NewCoins(sdk.NewCoin("test", math.ZeroInt())), + distributeFees: true, + wantErr: false, }, } for _, tc := range tests { t.Run(fmt.Sprintf("Case %s", tc.name), func(t *testing.T) { s := antesuite.SetupTestSuite(t, true) - acc := s.CreateTestAccounts(1)[0] - s.MockBankKeeper.On("SendCoinsFromAccountToModule", s.Ctx, acc.Account.GetAddress(), types.FeeCollectorName, tc.coins).Return(nil).Once() + if tc.distributeFees { + s.MockBankKeeper.On("SendCoinsFromModuleToModule", s.Ctx, types.FeeCollectorName, + authtypes.FeeCollectorName, + tc.coins).Return(nil).Once() + } - if err := post.DeductCoins(s.MockBankKeeper, s.Ctx, acc.Account, tc.coins, false); (err != nil) != tc.wantErr { + if err := post.DeductCoins(s.MockBankKeeper, s.Ctx, tc.coins, tc.distributeFees); (err != nil) != tc.wantErr { s.Errorf(err, "DeductCoins() error = %v, wantErr %v", err, tc.wantErr) } }) @@ -76,10 +103,10 @@ func TestDeductCoinsAndDistribute(t *testing.T) { for _, tc := range tests { t.Run(fmt.Sprintf("Case %s", tc.name), func(t *testing.T) { s := antesuite.SetupTestSuite(t, true) - acc := s.CreateTestAccounts(1)[0] - s.MockBankKeeper.On("SendCoinsFromAccountToModule", s.Ctx, acc.Account.GetAddress(), authtypes.FeeCollectorName, tc.coins).Return(nil).Once() + s.MockBankKeeper.On("SendCoinsFromModuleToModule", s.Ctx, types.FeeCollectorName, authtypes.FeeCollectorName, + tc.coins).Return(nil).Once() - if err := post.DeductCoins(s.MockBankKeeper, s.Ctx, acc.Account, tc.coins, true); (err != nil) != tc.wantErr { + if err := post.DeductCoins(s.MockBankKeeper, s.Ctx, tc.coins, true); (err != nil) != tc.wantErr { s.Errorf(err, "DeductCoins() error = %v, wantErr %v", err, tc.wantErr) } }) @@ -112,9 +139,10 @@ func TestSendTip(t *testing.T) { t.Run(fmt.Sprintf("Case %s", tc.name), func(t *testing.T) { s := antesuite.SetupTestSuite(t, true) accs := s.CreateTestAccounts(2) - s.MockBankKeeper.On("SendCoins", s.Ctx, mock.Anything, mock.Anything, tc.coins).Return(nil).Once() + s.MockBankKeeper.On("SendCoinsFromModuleToAccount", s.Ctx, types.FeeCollectorName, mock.Anything, + tc.coins).Return(nil).Once() - if err := post.SendTip(s.MockBankKeeper, s.Ctx, accs[0].Account.GetAddress(), accs[1].Account.GetAddress(), tc.coins); (err != nil) != tc.wantErr { + if err := post.SendTip(s.MockBankKeeper, s.Ctx, accs[1].Account.GetAddress(), tc.coins); (err != nil) != tc.wantErr { s.Errorf(err, "SendTip() error = %v, wantErr %v", err, tc.wantErr) } }) @@ -126,7 +154,7 @@ func TestPostHandle(t *testing.T) { const ( baseDenom = "stake" resolvableDenom = "atom" - expectedConsumedGas = 31690 + expectedConsumedGas = 33468 gasLimit = expectedConsumedGas ) @@ -143,7 +171,8 @@ func TestPostHandle(t *testing.T) { Name: "signer has no funds", Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { accs := s.CreateTestAccounts(1) - s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[0].Account.GetAddress(), types.FeeCollectorName, mock.Anything).Return(sdkerrors.ErrInsufficientFunds) + s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[0].Account.GetAddress(), + types.FeeCollectorName, mock.Anything).Return(sdkerrors.ErrInsufficientFunds).Once() return antesuite.TestCaseArgs{ Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, @@ -161,7 +190,8 @@ func TestPostHandle(t *testing.T) { Name: "signer has no funds - simulate", Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { accs := s.CreateTestAccounts(1) - s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[0].Account.GetAddress(), types.FeeCollectorName, mock.Anything).Return(sdkerrors.ErrInsufficientFunds) + s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[0].Account.GetAddress(), + types.FeeCollectorName, mock.Anything).Return(sdkerrors.ErrInsufficientFunds).Once() return antesuite.TestCaseArgs{ Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, @@ -190,14 +220,15 @@ func TestPostHandle(t *testing.T) { RunPost: true, Simulate: false, ExpPass: false, - ExpErr: sdkerrors.ErrInvalidGasLimit, + ExpErr: sdkerrors.ErrOutOfGas, }, { Name: "0 gas given should pass - simulate", Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { accs := s.CreateTestAccounts(1) - s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[0].Account.GetAddress(), types.FeeCollectorName, mock.Anything).Return(nil) - s.MockBankKeeper.On("SendCoins", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() + s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[0].Account.GetAddress(), + types.FeeCollectorName, mock.Anything).Return(nil).Once() + s.MockBankKeeper.On("SendCoinsFromModuleToAccount", mock.Anything, types.FeeCollectorName, mock.Anything, mock.Anything).Return(nil).Once() return antesuite.TestCaseArgs{ Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, @@ -216,8 +247,9 @@ func TestPostHandle(t *testing.T) { Name: "signer has enough funds, should pass, no tip", Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { accs := s.CreateTestAccounts(1) - s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[0].Account.GetAddress(), types.FeeCollectorName, mock.Anything).Return(nil) - s.MockBankKeeper.On("SendCoins", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() + s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[0].Account.GetAddress(), + types.FeeCollectorName, mock.Anything).Return(nil) + s.MockBankKeeper.On("SendCoinsFromModuleToAccount", mock.Anything, types.FeeCollectorName, mock.Anything, mock.Anything).Return(nil).Once() return antesuite.TestCaseArgs{ Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, @@ -236,8 +268,9 @@ func TestPostHandle(t *testing.T) { Name: "signer has enough funds, should pass with tip", Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { accs := s.CreateTestAccounts(1) - s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[0].Account.GetAddress(), types.FeeCollectorName, mock.Anything).Return(nil) - s.MockBankKeeper.On("SendCoins", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() + s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[0].Account.GetAddress(), + types.FeeCollectorName, mock.Anything).Return(nil).Once() + s.MockBankKeeper.On("SendCoinsFromModuleToAccount", mock.Anything, types.FeeCollectorName, mock.Anything, mock.Anything).Return(nil).Once() return antesuite.TestCaseArgs{ Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, @@ -256,8 +289,9 @@ func TestPostHandle(t *testing.T) { Name: "signer has enough funds, should pass with tip - simulate", Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { accs := s.CreateTestAccounts(1) - s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[0].Account.GetAddress(), types.FeeCollectorName, mock.Anything).Return(nil) - s.MockBankKeeper.On("SendCoins", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() + s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[0].Account.GetAddress(), + types.FeeCollectorName, mock.Anything).Return(nil).Once() + s.MockBankKeeper.On("SendCoinsFromModuleToAccount", mock.Anything, types.FeeCollectorName, mock.Anything, mock.Anything).Return(nil).Once() return antesuite.TestCaseArgs{ Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, @@ -272,12 +306,57 @@ func TestPostHandle(t *testing.T) { ExpErr: nil, ExpectConsumedGas: expectedConsumedGas, }, + { + Name: "fee market is enabled during the transaction - should pass and skip deduction until next block", + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + + // disable fee market before tx + s.Ctx = s.Ctx.WithBlockHeight(10) + disabledParams := types.DefaultParams() + disabledParams.Enabled = false + err := s.FeeMarketKeeper.SetParams(s.Ctx, disabledParams) + s.Require().NoError(err) + + s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[0].Account.GetAddress(), + authtypes.FeeCollectorName, mock.Anything).Return(nil).Once() + + return antesuite.TestCaseArgs{ + Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, + GasLimit: gasLimit, + FeeAmount: validResolvableFee, + } + }, + StateUpdate: func(s *antesuite.TestSuite) { + // enable the fee market + enabledParams := types.DefaultParams() + req := &types.MsgParams{ + Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + Params: enabledParams, + } + + _, err := s.MsgServer.Params(s.Ctx, req) + s.Require().NoError(err) + + height, err := s.FeeMarketKeeper.GetEnabledHeight(s.Ctx) + s.Require().NoError(err) + s.Require().Equal(int64(10), height) + }, + RunAnte: true, + RunPost: true, + Simulate: false, + ExpPass: true, + ExpErr: nil, + ExpectConsumedGas: 43327, // extra gas consumed because msg server is run + }, { Name: "signer has enough funds, should pass, no tip - resolvable denom", Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { accs := s.CreateTestAccounts(1) - s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[0].Account.GetAddress(), types.FeeCollectorName, mock.Anything).Return(nil) - s.MockBankKeeper.On("SendCoins", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() + s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[0].Account.GetAddress(), + types.FeeCollectorName, mock.Anything).Return(nil).Once() + s.MockBankKeeper.On("SendCoinsFromModuleToAccount", mock.Anything, types.FeeCollectorName, mock.Anything, + mock.Anything).Return(nil).Once() return antesuite.TestCaseArgs{ Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, @@ -296,8 +375,9 @@ func TestPostHandle(t *testing.T) { Name: "signer has enough funds, should pass, no tip - resolvable denom - simulate", Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { accs := s.CreateTestAccounts(1) - s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[0].Account.GetAddress(), types.FeeCollectorName, mock.Anything).Return(nil) - s.MockBankKeeper.On("SendCoins", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() + s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[0].Account.GetAddress(), + types.FeeCollectorName, mock.Anything).Return(nil).Once() + s.MockBankKeeper.On("SendCoinsFromModuleToAccount", mock.Anything, types.FeeCollectorName, mock.Anything, mock.Anything).Return(nil).Once() return antesuite.TestCaseArgs{ Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, @@ -316,8 +396,9 @@ func TestPostHandle(t *testing.T) { Name: "signer has enough funds, should pass with tip - resolvable denom", Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { accs := s.CreateTestAccounts(1) - s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[0].Account.GetAddress(), types.FeeCollectorName, mock.Anything).Return(nil) - s.MockBankKeeper.On("SendCoins", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() + s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[0].Account.GetAddress(), + types.FeeCollectorName, mock.Anything).Return(nil).Once() + s.MockBankKeeper.On("SendCoinsFromModuleToAccount", mock.Anything, types.FeeCollectorName, mock.Anything, mock.Anything).Return(nil).Once() return antesuite.TestCaseArgs{ Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, @@ -336,8 +417,9 @@ func TestPostHandle(t *testing.T) { Name: "signer has enough funds, should pass with tip - resolvable denom - simulate", Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { accs := s.CreateTestAccounts(1) - s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[0].Account.GetAddress(), types.FeeCollectorName, mock.Anything).Return(nil) - s.MockBankKeeper.On("SendCoins", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() + s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[0].Account.GetAddress(), + types.FeeCollectorName, mock.Anything).Return(nil).Once() + s.MockBankKeeper.On("SendCoinsFromModuleToAccount", mock.Anything, types.FeeCollectorName, mock.Anything, mock.Anything).Return(nil).Once() return antesuite.TestCaseArgs{ Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, @@ -354,9 +436,10 @@ func TestPostHandle(t *testing.T) { }, { Name: "0 gas given should pass in simulate - no fee", - Malleate: func(suite *antesuite.TestSuite) antesuite.TestCaseArgs { - accs := suite.CreateTestAccounts(1) - + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[0].Account.GetAddress(), + types.FeeCollectorName, mock.Anything).Return(nil).Once() return antesuite.TestCaseArgs{ Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, GasLimit: 0, @@ -372,9 +455,10 @@ func TestPostHandle(t *testing.T) { }, { Name: "0 gas given should pass in simulate - fee", - Malleate: func(suite *antesuite.TestSuite) antesuite.TestCaseArgs { - accs := suite.CreateTestAccounts(1) - + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + s.MockBankKeeper.On("SendCoinsFromAccountToModule", mock.Anything, accs[0].Account.GetAddress(), + types.FeeCollectorName, mock.Anything).Return(nil).Once() return antesuite.TestCaseArgs{ Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, GasLimit: 0, @@ -420,7 +504,7 @@ func TestPostHandle(t *testing.T) { RunPost: true, Simulate: false, ExpPass: false, - ExpErr: sdkerrors.ErrInvalidGasLimit, + ExpErr: sdkerrors.ErrOutOfGas, }, } diff --git a/x/feemarket/post/mocks/mock_bank_keeper.go b/x/feemarket/post/mocks/mock_bank_keeper.go index 9f6a55d..1bd4092 100644 --- a/x/feemarket/post/mocks/mock_bank_keeper.go +++ b/x/feemarket/post/mocks/mock_bank_keeper.go @@ -74,6 +74,42 @@ func (_m *BankKeeper) SendCoinsFromAccountToModule(ctx types.Context, senderAddr return r0 } +// SendCoinsFromModuleToAccount provides a mock function with given fields: ctx, senderModule, recipientAddr, amt +func (_m *BankKeeper) SendCoinsFromModuleToAccount(ctx types.Context, senderModule string, recipientAddr types.AccAddress, amt types.Coins) error { + ret := _m.Called(ctx, senderModule, recipientAddr, amt) + + if len(ret) == 0 { + panic("no return value specified for SendCoinsFromModuleToAccount") + } + + var r0 error + if rf, ok := ret.Get(0).(func(types.Context, string, types.AccAddress, types.Coins) error); ok { + r0 = rf(ctx, senderModule, recipientAddr, amt) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// SendCoinsFromModuleToModule provides a mock function with given fields: ctx, senderModule, recipientModule, amt +func (_m *BankKeeper) SendCoinsFromModuleToModule(ctx types.Context, senderModule string, recipientModule string, amt types.Coins) error { + ret := _m.Called(ctx, senderModule, recipientModule, amt) + + if len(ret) == 0 { + panic("no return value specified for SendCoinsFromModuleToModule") + } + + var r0 error + if rf, ok := ret.Get(0).(func(types.Context, string, string, types.Coins) error); ok { + r0 = rf(ctx, senderModule, recipientModule, amt) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // NewBankKeeper creates a new instance of BankKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewBankKeeper(t interface { diff --git a/x/feemarket/post/mocks/mock_feemarket_keeper.go b/x/feemarket/post/mocks/mock_feemarket_keeper.go index c767bb5..0d69365 100644 --- a/x/feemarket/post/mocks/mock_feemarket_keeper.go +++ b/x/feemarket/post/mocks/mock_feemarket_keeper.go @@ -15,6 +15,34 @@ type FeeMarketKeeper struct { mock.Mock } +// GetEnabledHeight provides a mock function with given fields: ctx +func (_m *FeeMarketKeeper) GetEnabledHeight(ctx types.Context) (int64, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for GetEnabledHeight") + } + + var r0 int64 + var r1 error + if rf, ok := ret.Get(0).(func(types.Context) (int64, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(types.Context) int64); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(int64) + } + + if rf, ok := ret.Get(1).(func(types.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetMinGasPrice provides a mock function with given fields: ctx, denom func (_m *FeeMarketKeeper) GetMinGasPrice(ctx types.Context, denom string) (types.DecCoin, error) { ret := _m.Called(ctx, denom) diff --git a/x/feemarket/types/keys.go b/x/feemarket/types/keys.go index 78a84f4..8bbd54c 100644 --- a/x/feemarket/types/keys.go +++ b/x/feemarket/types/keys.go @@ -6,13 +6,14 @@ const ( // StoreKey is the store key string for the feemarket module. StoreKey = ModuleName - // FeeCollectorName the root string for the fee market fee collector account address. + // FeeCollectorName is the root string for the fee market fee collector account address. FeeCollectorName = "feemarket-fee-collector" ) const ( prefixParams = iota + 1 prefixState + prefixEnableHeight = 3 ) var ( @@ -22,6 +23,9 @@ var ( // KeyState is the store key for the feemarket module's data. KeyState = []byte{prefixState} + // KeyEnabledHeight is the store key for the feemarket module's enabled height. + KeyEnabledHeight = []byte{prefixEnableHeight} + EventTypeFeePay = "fee_pay" EventTypeTipPay = "tip_pay" AttributeKeyTip = "tip"