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 8bdf3da..6c1f199 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,12 @@ module github.com/skip-mev/feemarket -go 1.22.3 +go 1.22.6 require ( cosmossdk.io/api v0.7.5 cosmossdk.io/client/v2 v2.0.0-00010101000000-000000000000 - cosmossdk.io/core v0.11.0 - cosmossdk.io/depinject v1.0.0-alpha.4 + cosmossdk.io/core v0.11.1 + cosmossdk.io/depinject v1.0.0 cosmossdk.io/errors v1.0.1 cosmossdk.io/log v1.3.1 cosmossdk.io/math v1.3.0 @@ -16,14 +16,14 @@ require ( cosmossdk.io/x/evidence v0.1.1 cosmossdk.io/x/feegrant v0.1.1 cosmossdk.io/x/nft v0.1.1 - cosmossdk.io/x/tx v0.13.3 - cosmossdk.io/x/upgrade v0.1.3 + cosmossdk.io/x/tx v0.13.4 + cosmossdk.io/x/upgrade v0.1.4 github.com/client9/misspell v0.3.4 - github.com/cometbft/cometbft v0.38.8 + github.com/cometbft/cometbft v0.38.11 github.com/cosmos/cosmos-db v1.0.2 github.com/cosmos/cosmos-proto v1.0.0-beta.5 - github.com/cosmos/cosmos-sdk v0.50.7 - github.com/cosmos/gogoproto v1.5.0 + github.com/cosmos/cosmos-sdk v0.50.9 + github.com/cosmos/gogoproto v1.6.0 github.com/golang/protobuf v1.5.4 github.com/golangci/golangci-lint v1.59.1 github.com/grpc-ecosystem/grpc-gateway v1.16.0 @@ -33,9 +33,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.1 + 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 @@ -222,7 +222,6 @@ require ( github.com/ldez/tagliatelle v0.5.0 // indirect 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.14 // indirect github.com/lufeee/execinquery v1.2.1 // indirect github.com/macabu/inamedparam v0.1.3 // indirect @@ -323,20 +322,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 1f96b18..b3344af 100644 --- a/go.sum +++ b/go.sum @@ -196,10 +196,10 @@ cosmossdk.io/client/v2 v2.0.0-beta.1.0.20240124105859-5ad1805d0e79 h1:Hr1t0fCq1n cosmossdk.io/client/v2 v2.0.0-beta.1.0.20240124105859-5ad1805d0e79/go.mod h1:8pN6LSVReNnIxrC2QGcvuIJ/m1pJN6FNYn2kAYtYftI= cosmossdk.io/collections v0.4.0 h1:PFmwj2W8szgpD5nOd8GWH6AbYNi1f2J6akWXJ7P5t9s= cosmossdk.io/collections v0.4.0/go.mod h1:oa5lUING2dP+gdDquow+QjlF45eL1t4TJDypgGd+tv0= -cosmossdk.io/core v0.11.0 h1:vtIafqUi+1ZNAE/oxLOQQ7Oek2n4S48SWLG8h/+wdbo= -cosmossdk.io/core v0.11.0/go.mod h1:LaTtayWBSoacF5xNzoF8tmLhehqlA9z1SWiPuNC6X1w= -cosmossdk.io/depinject v1.0.0-alpha.4 h1:PLNp8ZYAMPTUKyG9IK2hsbciDWqna2z1Wsl98okJopc= -cosmossdk.io/depinject v1.0.0-alpha.4/go.mod h1:HeDk7IkR5ckZ3lMGs/o91AVUc7E596vMaOmslGFM3yU= +cosmossdk.io/core v0.11.1 h1:h9WfBey7NAiFfIcUhDVNS503I2P2HdZLebJlUIs8LPA= +cosmossdk.io/core v0.11.1/go.mod h1:OJzxcdC+RPrgGF8NJZR2uoQr56tc7gfBKhiKeDO7hH0= +cosmossdk.io/depinject v1.0.0 h1:dQaTu6+O6askNXO06+jyeUAnF2/ssKwrrszP9t5q050= +cosmossdk.io/depinject v1.0.0/go.mod h1:zxK/h3HgHoA/eJVtiSsoaRaRA2D5U4cJ5thIG4ssbB8= cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0= cosmossdk.io/errors v1.0.1/go.mod h1:MeelVSZThMi4bEakzhhhE/CKqVv3nOJDA25bIqRDu/U= cosmossdk.io/log v1.3.1 h1:UZx8nWIkfbbNEWusZqzAx3ZGvu54TZacWib3EzUYmGI= @@ -218,10 +218,10 @@ cosmossdk.io/x/feegrant v0.1.1 h1:EKFWOeo/pup0yF0svDisWWKAA9Zags6Zd0P3nRvVvw8= cosmossdk.io/x/feegrant v0.1.1/go.mod h1:2GjVVxX6G2fta8LWj7pC/ytHjryA6MHAJroBWHFNiEQ= cosmossdk.io/x/nft v0.1.1 h1:pslAVS8P5NkW080+LWOamInjDcq+v2GSCo+BjN9sxZ8= cosmossdk.io/x/nft v0.1.1/go.mod h1:Kac6F6y2gsKvoxU+fy8uvxRTi4BIhLOor2zgCNQwVgY= -cosmossdk.io/x/tx v0.13.3 h1:Ha4mNaHmxBc6RMun9aKuqul8yHiL78EKJQ8g23Zf73g= -cosmossdk.io/x/tx v0.13.3/go.mod h1:I8xaHv0rhUdIvIdptKIqzYy27+n2+zBVaxO6fscFhys= -cosmossdk.io/x/upgrade v0.1.3 h1:q4XpXc6zp0dX6x74uBtfN6+J7ikaQev5Bla6Q0ADLK8= -cosmossdk.io/x/upgrade v0.1.3/go.mod h1:jOdQhnaY5B8CDUoUbed23/Lre0Dk+r6BMQE40iKlVVQ= +cosmossdk.io/x/tx v0.13.4 h1:Eg0PbJgeO0gM8p5wx6xa0fKR7hIV6+8lC56UrsvSo0Y= +cosmossdk.io/x/tx v0.13.4/go.mod h1:BkFqrnGGgW50Y6cwTy+JvgAhiffbGEKW6KF9ufcDpvk= +cosmossdk.io/x/upgrade v0.1.4 h1:/BWJim24QHoXde8Bc64/2BSEB6W4eTydq0X/2f8+g38= +cosmossdk.io/x/upgrade v0.1.4/go.mod h1:9v0Aj+fs97O+Ztw+tG3/tp5JSlrmT7IcFhAebQHmOPo= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= @@ -405,8 +405,8 @@ github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZ 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/cometbft/cometbft v0.38.8 h1:XyJ9Cu3xqap6xtNxiemrO8roXZ+KS2Zlu7qQ0w1trvU= -github.com/cometbft/cometbft v0.38.8/go.mod h1:xOoGZrtUT+A5izWfHSJgl0gYZUE7lu7Z2XIS1vWG/QQ= +github.com/cometbft/cometbft v0.38.11 h1:6bNDUB8/xq4uYonYwIfGc9OqK1ZH4NkdaMmR1LZIJqk= +github.com/cometbft/cometbft v0.38.11/go.mod h1:jHPx9vQpWzPHEAiYI/7EDKaB1NXhK6o3SArrrY8ExKc= github.com/cometbft/cometbft-db v0.9.1 h1:MIhVX5ja5bXNHF8EYrThkG9F7r9kSfv8BX4LWaxWJ4M= github.com/cometbft/cometbft-db v0.9.1/go.mod h1:iliyWaoV0mRwBJoizElCwwRA9Tf7jZJOURcRZF9m60U= github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= @@ -423,15 +423,15 @@ github.com/cosmos/cosmos-db v1.0.2 h1:hwMjozuY1OlJs/uh6vddqnk9j7VamLv+0DBlbEXbAK 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.50.7 h1:LsBGKxifENR/DN4E1RZaitsyL93HU44x0p8EnMHp4V4= -github.com/cosmos/cosmos-sdk v0.50.7/go.mod h1:84xDDJEHttRT7NDGwBaUOLVOMN0JNE9x7NbsYIxXs1s= +github.com/cosmos/cosmos-sdk v0.50.9 h1:gt2usjz0H0qW6KwAxWw7ZJ3XU8uDwmhN+hYG3nTLeSg= +github.com/cosmos/cosmos-sdk v0.50.9/go.mod h1:TMH6wpoYBcg7Cp5BEg8fneLr+8XloNQkf2MRNF9V6JE= github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= github.com/cosmos/gogogateway v1.2.0 h1:Ae/OivNhp8DqBi/sh2A8a1D0y638GpL3tkmLQAiKxTE= github.com/cosmos/gogogateway v1.2.0/go.mod h1:iQpLkGWxYcnCdz5iAdLcRBSw3h7NXeOkZ4GUkT+tbFI= github.com/cosmos/gogoproto v1.4.2/go.mod h1:cLxOsn1ljAHSV527CHOtaIP91kK6cCrZETRBrkzItWU= -github.com/cosmos/gogoproto v1.5.0 h1:SDVwzEqZDDBoslaeZg+dGE55hdzHfgUA40pEanMh52o= -github.com/cosmos/gogoproto v1.5.0/go.mod h1:iUM31aofn3ymidYG6bUR5ZFrk+Om8p5s754eMUcyp8I= +github.com/cosmos/gogoproto v1.6.0 h1:Xm0F/96O5Ox4g6xGgjA41rWaaPjYtOdTi59uBcV2qEE= +github.com/cosmos/gogoproto v1.6.0/go.mod h1:Y+g956rcUf2vr4uwtCcK/1Xx9BWVluCtcI9vsh0GHmk= github.com/cosmos/iavl v1.1.2 h1:zL9FK7C4L/P4IF1Dm5fIwz0WXCnn7Bp1M2FxH0ayM7Y= github.com/cosmos/iavl v1.1.2/go.mod h1:jLeUvm6bGT1YutCaL2fIar/8vGUE8cPZvh/gXEWDaDM= github.com/cosmos/ics23/go v0.10.0 h1:iXqLLgp2Lp+EdpIuwXTYIQU+AiHj9mOC2X9ab++bZDM= @@ -948,8 +948,6 @@ github.com/leonklingele/grouper v1.1.2 h1:o1ARBDLOmmasUaNDesWqWCIFH3u7hoFlM84Yrj github.com/leonklingele/grouper v1.1.2/go.mod h1:6D0M/HVkhs2yRKRFZUoGjeDy7EZTfFBE9gl4kjmIGkA= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= -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.14 h1:HTgyYalNwBSG/1qCQUIott44wU5b2Y9Kr3z7SK5OfGQ= @@ -1412,8 +1410,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/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-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1462,8 +1460,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= @@ -1527,8 +1525,8 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= 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.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= @@ -1571,8 +1569,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= @@ -1678,16 +1676,16 @@ golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.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= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= 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.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= @@ -1700,8 +1698,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 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.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= @@ -1786,8 +1784,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= @@ -1967,8 +1965,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= @@ -2010,8 +2008,8 @@ google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= -google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= +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 a4e37ce..f5aab81 100644 --- a/tests/app/app.go +++ b/tests/app/app.go @@ -520,6 +520,7 @@ func NewSimApp( anteOptions := AnteHandlerOptions{ BaseOptions: anteHandlerOptions, AccountKeeper: app.AccountKeeper, + BankKeeper: app.BankKeeper, FeeMarketKeeper: app.FeeMarketKeeper, } anteHandler, err := NewAnteHandler(anteOptions) @@ -530,7 +531,6 @@ func NewSimApp( postHandlerOptions := PostHandlerOptions{ AccountKeeper: app.AccountKeeper, BankKeeper: app.BankKeeper, - FeeGrantKeeper: app.FeeGrantKeeper, FeeMarketKeeper: app.FeeMarketKeeper, } postHandler, err := NewPostHandler(postHandlerOptions) diff --git a/tests/app/feemarketd/cmd/commands.go b/tests/app/feemarketd/cmd/commands.go index 8e113f7..86554b6 100644 --- a/tests/app/feemarketd/cmd/commands.go +++ b/tests/app/feemarketd/cmd/commands.go @@ -5,16 +5,10 @@ import ( "io" "os" - "github.com/skip-mev/feemarket/tests/app" - - cmtcfg "github.com/cometbft/cometbft/config" - dbm "github.com/cosmos/cosmos-db" - "github.com/spf13/cobra" - "github.com/spf13/viper" - "cosmossdk.io/log" confixcmd "cosmossdk.io/tools/confix/cmd" - + cmtcfg "github.com/cometbft/cometbft/config" + dbm "github.com/cosmos/cosmos-db" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/debug" "github.com/cosmos/cosmos-sdk/client/flags" @@ -30,6 +24,10 @@ import ( authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" "github.com/cosmos/cosmos-sdk/x/crisis" genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" + "github.com/spf13/cobra" + "github.com/spf13/viper" + + "github.com/skip-mev/feemarket/tests/app" ) // initCometBFTConfig helps to override default CometBFT Config values. 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 c432077..58e42eb 100644 --- a/tests/e2e/setup.go +++ b/tests/e2e/setup.go @@ -82,7 +82,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{}) @@ -91,6 +91,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() @@ -100,7 +123,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{}) @@ -119,7 +142,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,32 @@ func (s *TestSuite) SendCoinsMultiBroadcast(ctx context.Context, sender, receive } } - tx := s.CreateTx(s.chain, sender, fees.String(), gas, msgs...) + tx := s.CreateTx(s.chain, sender, fees.String(), gas, false, msgs...) // get an rpc endpoint for the chain c := s.chain.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) { + 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(s.chain, sender, fees.String(), gas, bumpSequence, msgs...) + + // get an rpc endpoint for the chain + c := s.chain.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) { resp, err := s.ExecTx( @@ -383,7 +425,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, @@ -393,7 +442,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. @@ -433,7 +481,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() @@ -457,6 +507,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 7cbeeed..25d7edb 100644 --- a/tests/e2e/suite.go +++ b/tests/e2e/suite.go @@ -521,7 +521,191 @@ func (s *TestSuite) TestSendTxIncrease() { 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) + + 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.TxResult.Code != 0) + s.T().Log(txResp.TxResult.Log) + s.Require().Contains(txResp.TxResult.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.TxResult.Code != 0) + s.T().Log(txResp.TxResult.Log) + s.Require().Contains(txResp.TxResult.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) + hash1 := txResp.Hash + + 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) + hash2 := txResp.Hash + + nodes := s.chain.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(s.chain, height+5) + + // after waiting, we can now query the Tx Responses + resp, err := nodes[0].TxHashToResponse(context.Background(), hash1.String()) + s.Require().NoError(err) + s.Require().NotNil(resp) + s.Require().True(resp.Code == 0) + + resp, err = nodes[0].TxHashToResponse(context.Background(), hash2.String()) + s.Require().NoError(err) + s.Require().NotNil(resp) + s.Require().True(resp.Code != 0) + s.Require().Contains(resp.RawLog, "error escrowing funds") + s.T().Log(resp.RawLog) + + // 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 a1164ff..6f15b49 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 aa147de..0f08ede 100644 --- a/x/feemarket/ante/expected_keepers.go +++ b/x/feemarket/ante/expected_keepers.go @@ -38,6 +38,8 @@ type BankKeeper interface { IsSendEnabledCoins(ctx context.Context, coins ...sdk.Coin) error SendCoins(ctx context.Context, from, to sdk.AccAddress, amt sdk.Coins) error SendCoinsFromAccountToModule(ctx context.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error + SendCoinsFromModuleToModule(ctx context.Context, senderModule, recipientModule string, amt sdk.Coins) error + SendCoinsFromModuleToAccount(ctx context.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..fc3ec60 100644 --- a/x/feemarket/ante/fee.go +++ b/x/feemarket/ante/fee.go @@ -1,6 +1,7 @@ package ante import ( + "bytes" "math" errorsmod "cosmossdk.io/errors" @@ -13,11 +14,17 @@ import ( 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 +44,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 +138,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 +175,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 sdk.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 88% rename from x/feemarket/post/feegrant_test.go rename to x/feemarket/ante/feegrant_test.go index 6084f80..6456416 100644 --- a/x/feemarket/post/feegrant_test.go +++ b/x/feemarket/ante/feegrant_test.go @@ -1,4 +1,4 @@ -package post_test +package ante_test import ( "context" @@ -7,11 +7,6 @@ import ( "time" "cosmossdk.io/x/feegrant" - - "github.com/stretchr/testify/mock" - - "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" @@ -20,16 +15,20 @@ import ( 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/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 +49,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 +77,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 +116,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 +129,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 +152,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 85e4f01..8121443 100644 --- a/x/feemarket/ante/mocks/mock_bank_keeper.go +++ b/x/feemarket/ante/mocks/mock_bank_keeper.go @@ -75,6 +75,42 @@ func (_m *BankKeeper) SendCoinsFromAccountToModule(ctx context.Context, senderAd return r0 } +// SendCoinsFromModuleToAccount provides a mock function with given fields: ctx, senderModule, recipientAddr, amt +func (_m *BankKeeper) SendCoinsFromModuleToAccount(ctx context.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(context.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 context.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(context.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 d996eef..ad0449d 100644 --- a/x/feemarket/ante/suite/suite.go +++ b/x/feemarket/ante/suite/suite.go @@ -3,6 +3,8 @@ package suite import ( "testing" + feemarketkeeper "github.com/skip-mev/feemarket/x/feemarket/keeper" + txsigning "cosmossdk.io/x/tx/signing" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/tx" @@ -40,13 +42,15 @@ 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 TestEncodingConfig + + MsgServer feemarkettypes.MsgServer } // TestAccount represents an account used in the tests in x/auth/ante. @@ -88,6 +92,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 @@ -96,6 +103,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 @@ -105,11 +113,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, ), ), @@ -123,7 +134,6 @@ func (s *TestSuite) SetupHandlers(mock bool) { feemarketpost.NewFeeMarketDeductDecorator( s.AccountKeeper, bankKeeper, - feeGrantKeeper, s.FeeMarketKeeper, ), } @@ -135,6 +145,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 @@ -176,21 +187,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) } - if tc.RunPost { - newCtx, handleErr = s.PostHandler(s.Ctx, tx, tc.Simulate, true) + // perform mid-tx state update if configured + if tc.StateUpdate != nil { + tc.StateUpdate(s) + } + + 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 @@ -205,9 +223,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") 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 cb2a901..f5bcb51 100644 --- a/x/feemarket/keeper/keeper.go +++ b/x/feemarket/keeper/keeper.go @@ -2,9 +2,11 @@ package keeper import ( "fmt" + "strconv" "cosmossdk.io/log" storetypes "cosmossdk.io/store/types" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -56,6 +58,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 d351e55..e9b9b5e 100644 --- a/x/feemarket/keeper/keeper_test.go +++ b/x/feemarket/keeper/keeper_test.go @@ -3,6 +3,7 @@ package keeper_test import ( "testing" + "cosmossdk.io/math" txsigning "cosmossdk.io/x/tx/signing" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" @@ -10,13 +11,11 @@ import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/std" - authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" - "github.com/cosmos/gogoproto/proto" - - "cosmossdk.io/math" 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/cosmos/gogoproto/proto" "github.com/stretchr/testify/suite" testkeeper "github.com/skip-mev/feemarket/testutils/keeper" @@ -55,6 +54,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() { @@ -120,6 +120,16 @@ func (s *KeeperTestSuite) TestParams() { }) } +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 { 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 0ffa2dd..4f01b64 100644 --- a/x/feemarket/module.go +++ b/x/feemarket/module.go @@ -116,7 +116,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 6f9db5c..a759656 100644 --- a/x/feemarket/post/expected_keeper.go +++ b/x/feemarket/post/expected_keeper.go @@ -22,13 +22,6 @@ type AccountKeeper interface { NewAccountWithAddress(ctx context.Context, addr sdk.AccAddress) sdk.AccountI } -// FeeGrantKeeper defines the expected feegrant keeper. -// -//go:generate mockery --name FeeGrantKeeper --filename mock_feegrant_keeper.go -type FeeGrantKeeper interface { - UseGrantedFees(ctx context.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 @@ -36,6 +29,8 @@ type BankKeeper interface { IsSendEnabledCoins(ctx context.Context, coins ...sdk.Coin) error SendCoins(ctx context.Context, from, to sdk.AccAddress, amt sdk.Coins) error SendCoinsFromAccountToModule(ctx context.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error + SendCoinsFromModuleToModule(ctx context.Context, senderModule, recipientModule string, amt sdk.Coins) error + SendCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error } // FeeMarketKeeper defines the expected feemarket keeper. @@ -48,4 +43,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 1b59755..8022691 100644 --- a/x/feemarket/post/fee.go +++ b/x/feemarket/post/fee.go @@ -1,7 +1,6 @@ package post import ( - "bytes" "fmt" "cosmossdk.io/math" @@ -25,15 +24,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, } } @@ -67,6 +64,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 { @@ -120,7 +127,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 } @@ -137,59 +144,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 !bytes.Equal(feeGranter, 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 } @@ -197,13 +164,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, deductFeesFromAcc.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 } @@ -211,7 +177,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, deductFeesFromAcc.String()), sdk.NewAttribute(feemarkettypes.AttributeKeyTipPayee, proposer.String()), )) } @@ -221,24 +186,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 sdk.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 4ed1f0e..af1cc8e 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", math.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 = 33339 + expectedConsumedGas = 35117 gasLimit = expectedConsumedGas ) @@ -142,7 +170,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())}, @@ -160,7 +189,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())}, @@ -189,14 +219,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())}, @@ -215,8 +246,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())}, @@ -235,8 +267,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())}, @@ -255,8 +288,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())}, @@ -271,12 +305,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: 44976, // 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())}, @@ -295,8 +374,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())}, @@ -315,8 +395,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())}, @@ -335,8 +416,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())}, @@ -353,9 +435,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, @@ -371,9 +454,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, @@ -419,7 +503,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 6c35acf..19cfc10 100644 --- a/x/feemarket/post/mocks/mock_bank_keeper.go +++ b/x/feemarket/post/mocks/mock_bank_keeper.go @@ -76,6 +76,42 @@ func (_m *BankKeeper) SendCoinsFromAccountToModule(ctx context.Context, senderAd return r0 } +// SendCoinsFromModuleToAccount provides a mock function with given fields: ctx, senderModule, recipientAddr, amt +func (_m *BankKeeper) SendCoinsFromModuleToAccount(ctx context.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(context.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 context.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(context.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_feegrant_keeper.go b/x/feemarket/post/mocks/mock_feegrant_keeper.go deleted file mode 100644 index f78b37e..0000000 --- a/x/feemarket/post/mocks/mock_feegrant_keeper.go +++ /dev/null @@ -1,51 +0,0 @@ -// Code generated by mockery v2.43.2. DO NOT EDIT. - -package mocks - -import ( - context "context" - - mock "github.com/stretchr/testify/mock" - - proto "github.com/cosmos/gogoproto/proto" - - types "github.com/cosmos/cosmos-sdk/types" -) - -// FeeGrantKeeper is an autogenerated mock type for the FeeGrantKeeper type -type FeeGrantKeeper struct { - mock.Mock -} - -// UseGrantedFees provides a mock function with given fields: ctx, granter, grantee, fee, msgs -func (_m *FeeGrantKeeper) UseGrantedFees(ctx context.Context, granter types.AccAddress, grantee types.AccAddress, fee types.Coins, msgs []proto.Message) error { - ret := _m.Called(ctx, granter, grantee, fee, msgs) - - if len(ret) == 0 { - panic("no return value specified for UseGrantedFees") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, types.AccAddress, types.AccAddress, types.Coins, []proto.Message) error); ok { - r0 = rf(ctx, granter, grantee, fee, msgs) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// NewFeeGrantKeeper creates a new instance of FeeGrantKeeper. 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 NewFeeGrantKeeper(t interface { - mock.TestingT - Cleanup(func()) -}, -) *FeeGrantKeeper { - mock := &FeeGrantKeeper{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} 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"