Skip to content

Commit 126af48

Browse files
committed
feat: custom ante handler to verify message creator
1 parent bed75e4 commit 126af48

File tree

7 files changed

+432
-15
lines changed

7 files changed

+432
-15
lines changed

.github/workflows/ci-test.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ jobs:
1212
- name: Set up Go
1313
uses: actions/setup-go@v4
1414
with:
15-
go-version: '1.20'
15+
go-version: '1.21.3'
1616
- name: Run tests
1717
run: go test -v ./...
1818
- name: Go lint
1919
uses: golangci/golangci-lint-action@v3
2020
with:
21-
version: v1.51.2
21+
version: v1.55.2
2222
args: --verbose
2323
# Optional: if set to true then the all caching functionality will be complete disabled,
2424
# takes precedence over all other caching options.

app/app.go

+12-1
Original file line numberDiff line numberDiff line change
@@ -913,7 +913,7 @@ func New(
913913
app.SetInitChainer(app.InitChainer)
914914
app.SetBeginBlocker(app.BeginBlocker)
915915

916-
anteHandler, err := ante.NewAnteHandler(
916+
baseAnteHandler, err := ante.NewAnteHandler(
917917
ante.HandlerOptions{
918918
AccountKeeper: app.AccountKeeper,
919919
BankKeeper: app.BankKeeper,
@@ -925,6 +925,17 @@ func New(
925925
if err != nil {
926926
panic(err)
927927
}
928+
anteDecorators := []sdk.AnteDecorator{
929+
palomamodule.NewAnteHandlerDecorator(baseAnteHandler),
930+
}
931+
anteDecorators = append(anteDecorators,
932+
palomamodule.NewLogMsgDecorator(app.appCodec),
933+
palomamodule.NewVerifyAuthorisedSignatureDecorator(app.FeeGrantKeeper),
934+
)
935+
936+
anteHandler := sdk.ChainAnteDecorators(
937+
anteDecorators...,
938+
)
928939

929940
app.SetAnteHandler(anteHandler)
930941
app.SetEndBlocker(app.EndBlocker)

util/libmeta/get_signers_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ func Test_GetSigners(t *testing.T) {
4141
}{
4242
{
4343
name: "with one valid signer",
44-
signers: []string{sdk.AccAddress("addres-1").String()},
44+
signers: []string{sdk.AccAddress("address-1").String()},
4545
creator: "",
4646
panics: false,
4747
},
@@ -53,13 +53,13 @@ func Test_GetSigners(t *testing.T) {
5353
},
5454
{
5555
name: "with multiple valid signers",
56-
signers: []string{sdk.AccAddress("addres-1").String(), sdk.AccAddress("addres-2").String(), sdk.AccAddress("addres-3").String()},
56+
signers: []string{sdk.AccAddress("address-1").String(), sdk.AccAddress("address-2").String(), sdk.AccAddress("address-3").String()},
5757
creator: "",
5858
panics: false,
5959
},
6060
{
6161
name: "with an invalid signer among multiple valid signers",
62-
signers: []string{"foo", sdk.AccAddress("addres-2").String(), sdk.AccAddress("addres-3").String()},
62+
signers: []string{"foo", sdk.AccAddress("address-2").String(), sdk.AccAddress("address-3").String()},
6363
creator: "",
6464
panics: true,
6565
},

util/libmeta/validate_basic_test.go

+9-9
Original file line numberDiff line numberDiff line change
@@ -19,37 +19,37 @@ func Test_GetValidateBasic(t *testing.T) {
1919
{
2020
name: "with missing signers",
2121
signers: []string{},
22-
creator: sdk.AccAddress("addres-1").String(),
22+
creator: sdk.AccAddress("address-1").String(),
2323
errors: true,
2424
},
2525
{
2626
name: "with one valid signer and creator",
27-
signers: []string{sdk.AccAddress("addres-1").String()},
28-
creator: sdk.AccAddress("addres-1").String(),
27+
signers: []string{sdk.AccAddress("address-1").String()},
28+
creator: sdk.AccAddress("address-1").String(),
2929
errors: false,
3030
},
3131
{
3232
name: "with one valid signer and invalid creator",
33-
signers: []string{sdk.AccAddress("addres-1").String()},
33+
signers: []string{sdk.AccAddress("address-1").String()},
3434
creator: "foo",
3535
errors: true,
3636
},
3737
{
3838
name: "with multiple valid signers and creator",
39-
signers: []string{sdk.AccAddress("addres-1").String(), sdk.AccAddress("addres-2").String(), sdk.AccAddress("addres-3").String()},
40-
creator: sdk.AccAddress("addres-1").String(),
39+
signers: []string{sdk.AccAddress("address-1").String(), sdk.AccAddress("address-2").String(), sdk.AccAddress("address-3").String()},
40+
creator: sdk.AccAddress("address-1").String(),
4141
errors: false,
4242
},
4343
{
4444
name: "with multiple valid signers and invalid creator",
45-
signers: []string{sdk.AccAddress("addres-1").String(), sdk.AccAddress("addres-2").String(), sdk.AccAddress("addres-3").String()},
45+
signers: []string{sdk.AccAddress("address-1").String(), sdk.AccAddress("address-2").String(), sdk.AccAddress("address-3").String()},
4646
creator: "foo",
4747
errors: true,
4848
},
4949
{
5050
name: "with faulty signer among multiple valid signers and creator",
51-
signers: []string{sdk.AccAddress("addres-1").String(), "foo", sdk.AccAddress("addres-3").String()},
52-
creator: sdk.AccAddress("addres-1").String(),
51+
signers: []string{sdk.AccAddress("address-1").String(), "foo", sdk.AccAddress("address-3").String()},
52+
creator: sdk.AccAddress("address-1").String(),
5353
errors: true,
5454
},
5555
} {

x/paloma/ante.go

+142
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
package paloma
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/cometbft/cometbft/libs/log"
7+
"github.com/cosmos/cosmos-sdk/codec"
8+
sdk "github.com/cosmos/cosmos-sdk/types"
9+
"github.com/cosmos/cosmos-sdk/x/feegrant"
10+
"github.com/gogo/protobuf/proto"
11+
"github.com/palomachain/paloma/util/libmeta"
12+
"github.com/palomachain/paloma/x/paloma/types"
13+
vtypes "github.com/palomachain/paloma/x/valset/types"
14+
)
15+
16+
func logger(ctx sdk.Context) log.Logger {
17+
return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName))
18+
}
19+
20+
// HandlerDecorator is an ante decorator wrapper for an ante handler
21+
type HandlerDecorator struct {
22+
handler sdk.AnteHandler
23+
}
24+
25+
// NewAnteHandlerDecorator constructor for HandlerDecorator
26+
func NewAnteHandlerDecorator(handler sdk.AnteHandler) HandlerDecorator {
27+
return HandlerDecorator{handler}
28+
}
29+
30+
// AnteHandle wraps the next AnteHandler to perform custom pre- and post-processing
31+
func (decorator HandlerDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
32+
if newCtx, err = decorator.handler(ctx, tx, simulate); err != nil {
33+
return newCtx, err
34+
}
35+
36+
return next(newCtx, tx, simulate)
37+
}
38+
39+
// LogMsgDecorator logs all messages in blocks
40+
type LogMsgDecorator struct {
41+
cdc codec.Codec
42+
}
43+
44+
// NewLogMsgDecorator is the constructor for LogMsgDecorator
45+
func NewLogMsgDecorator(cdc codec.Codec) LogMsgDecorator {
46+
return LogMsgDecorator{cdc: cdc}
47+
}
48+
49+
// AnteHandle logs all messages in blocks
50+
func (d LogMsgDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
51+
if simulate || ctx.IsCheckTx() {
52+
return next(ctx, tx, simulate)
53+
}
54+
55+
msgs := tx.GetMsgs()
56+
57+
for _, msg := range msgs {
58+
logger(ctx).Debug(fmt.Sprintf("received message of type %s in block %d: %s",
59+
proto.MessageName(msg),
60+
ctx.BlockHeight(),
61+
string(d.cdc.MustMarshalJSON(msg)),
62+
))
63+
}
64+
65+
return next(ctx, tx, simulate)
66+
}
67+
68+
// VerifyAuthorisedSignatureDecorator verifies that the message is signed by at least one signature that has
69+
// active fee grant from the creator address, IF it contains metadata.
70+
type VerifyAuthorisedSignatureDecorator struct {
71+
fk types.FeegrantKeeper
72+
}
73+
74+
func NewVerifyAuthorisedSignatureDecorator(fk types.FeegrantKeeper) VerifyAuthorisedSignatureDecorator {
75+
return VerifyAuthorisedSignatureDecorator{fk: fk}
76+
}
77+
78+
// AnteHandle verifies that the message is signed by at least one signature that has
79+
// active fee grant from the creator address, IF the message contains metadata.
80+
func (d VerifyAuthorisedSignatureDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
81+
if simulate || ctx.IsCheckTx() {
82+
return next(ctx, tx, simulate)
83+
}
84+
85+
for _, msg := range tx.GetMsgs() {
86+
m, ok := msg.(libmeta.MsgWithMetadata[vtypes.MsgMetadata])
87+
if !ok {
88+
logger(ctx).Debug(fmt.Sprintf("msg %s does not contain metadata. skipping ownership verification...", proto.MessageName(msg)))
89+
continue
90+
}
91+
92+
creator := m.GetMetadata().GetCreator()
93+
signers := msg.GetSigners()
94+
95+
signedByCreator := func() bool {
96+
for _, v := range signers {
97+
if v.String() == creator {
98+
return true
99+
}
100+
}
101+
return false
102+
}()
103+
if signedByCreator {
104+
logger(ctx).Debug(fmt.Sprintf("msg %s was signed by creator.", proto.MessageName(msg)))
105+
continue
106+
}
107+
108+
grants, err := d.fk.AllowancesByGranter(ctx, &feegrant.QueryAllowancesByGranterRequest{
109+
Granter: creator,
110+
})
111+
if err != nil {
112+
return ctx, fmt.Errorf("failed to verify message signature authorisation: %w", err)
113+
}
114+
115+
logger(ctx).Debug(fmt.Sprintf("got %d allowances from granter %s", len(grants.GetAllowances()), creator))
116+
grantsLkUp := map[string]feegrant.Grant{}
117+
for _, v := range grants.GetAllowances() {
118+
if v == nil {
119+
continue
120+
}
121+
122+
grantsLkUp[v.GetGrantee()] = *v
123+
}
124+
125+
logger(ctx).Debug("grant lookup built", "map", grantsLkUp)
126+
grantees := make([]string, 0, len(signers))
127+
for _, signer := range signers {
128+
if v, found := grantsLkUp[signer.String()]; found {
129+
logger(ctx).Debug("found granted signature", "signature", v.Grantee)
130+
grantees = append(grantees, v.Grantee)
131+
}
132+
}
133+
134+
if len(grantees) < 1 {
135+
return ctx, fmt.Errorf("no signature from granted address found for message %s", proto.MessageName(msg))
136+
}
137+
138+
logger(ctx).Debug(fmt.Sprintf("found total of %d signatures from granted addresses for message %s", len(grantees), proto.MessageName(msg)))
139+
}
140+
141+
return next(ctx, tx, simulate)
142+
}

0 commit comments

Comments
 (0)