Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improve account relationship mapping #100

Merged
merged 10 commits into from
Jul 24, 2023
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
343 changes: 29 additions & 314 deletions modules/messages/account_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,12 @@ import (
"fmt"
"strings"

govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
"github.com/cosmos/gogoproto/proto"

crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types"
evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types"
govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types"
ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types"
channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types"

"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
authztypes "github.com/cosmos/cosmos-sdk/x/authz"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/forbole/juno/v5/types/config"

"github.com/forbole/juno/v5/types"
)

// MessageNotSupported returns an error telling that the given message is not supported
Expand All @@ -29,323 +19,48 @@ func MessageNotSupported(msg sdk.Msg) error {

// MessageAddressesParser represents a function that extracts all the
// involved addresses from a provided message (both accounts and validators)
type MessageAddressesParser = func(cdc codec.Codec, msg sdk.Msg) ([]string, error)

// JoinMessageParsers joins together all the given parsers, calling them in order
func JoinMessageParsers(parsers ...MessageAddressesParser) MessageAddressesParser {
return func(cdc codec.Codec, msg sdk.Msg) ([]string, error) {
for _, parser := range parsers {
// Try getting the addresses
addresses, _ := parser(cdc, msg)

// If some addresses are found, return them
if len(addresses) > 0 {
return addresses, nil
}
}
return nil, MessageNotSupported(msg)
}
}
type MessageAddressesParser = func(tx *types.Tx) ([]string, error)

// CosmosMessageAddressesParser represents a MessageAddressesParser that parses a
// Chain message and returns all the involved addresses (both accounts and validators)
var CosmosMessageAddressesParser = JoinMessageParsers(
BankMessagesParser,
CrisisMessagesParser,
DistributionMessagesParser,
EvidenceMessagesParser,
GovV1Beta1MessagesParser,
GovV1MessageParser,
IBCTransferMessagesParser,
SlashingMessagesParser,
StakingMessagesParser,
AuthzMessageParser,
DefaultMessagesParser,
)
var CosmosMessageAddressesParser = DefaultMessagesParser

// DefaultMessagesParser represents the default messages parser that simply returns the list
// of all the signers of a message
func DefaultMessagesParser(_ codec.Codec, cosmosMsg sdk.Msg) ([]string, error) {
var signers = make([]string, len(cosmosMsg.GetSigners()))
for index, signer := range cosmosMsg.GetSigners() {
signers[index] = signer.String()
}
return signers, nil
}

// BankMessagesParser returns the list of all the accounts involved in the given
// message if it's related to the x/bank module
func BankMessagesParser(_ codec.Codec, cosmosMsg sdk.Msg) ([]string, error) {
switch msg := cosmosMsg.(type) {
case *banktypes.MsgSend:
return []string{msg.ToAddress, msg.FromAddress}, nil

case *banktypes.MsgMultiSend:
var addresses []string
for _, i := range msg.Inputs {
addresses = append(addresses, i.Address)
}
for _, o := range msg.Outputs {
addresses = append(addresses, o.Address)
}
return addresses, nil
}

return nil, MessageNotSupported(cosmosMsg)
}

// CrisisMessagesParser returns the list of all the accounts involved in the given
// message if it's related to the x/crisis module
func CrisisMessagesParser(_ codec.Codec, cosmosMsg sdk.Msg) ([]string, error) {
switch msg := cosmosMsg.(type) {

case *crisistypes.MsgVerifyInvariant:
return []string{msg.Sender}, nil

}

return nil, MessageNotSupported(cosmosMsg)
}

// DistributionMessagesParser returns the list of all the accounts involved in the given
// message if it's related to the x/distribution module
func DistributionMessagesParser(_ codec.Codec, cosmosMsg sdk.Msg) ([]string, error) {
switch msg := cosmosMsg.(type) {

case *distrtypes.MsgSetWithdrawAddress:
return []string{msg.DelegatorAddress, msg.WithdrawAddress}, nil

case *distrtypes.MsgWithdrawDelegatorReward:
return []string{msg.DelegatorAddress, msg.ValidatorAddress}, nil

case *distrtypes.MsgWithdrawValidatorCommission:
return []string{msg.ValidatorAddress}, nil

case *distrtypes.MsgFundCommunityPool:
return []string{msg.Depositor}, nil

}

return nil, MessageNotSupported(cosmosMsg)
}

// EvidenceMessagesParser returns the list of all the accounts involved in the given
// message if it's related to the x/evidence module
func EvidenceMessagesParser(_ codec.Codec, cosmosMsg sdk.Msg) ([]string, error) {
switch msg := cosmosMsg.(type) {

case *evidencetypes.MsgSubmitEvidence:
return []string{msg.Submitter}, nil

}

return nil, MessageNotSupported(cosmosMsg)
}

// GovV1Beta1MessagesParser returns the list of all the accounts involved in the given
// message if it's related to the x/gov v1beta1 module
func GovV1Beta1MessagesParser(cdc codec.Codec, cosmosMsg sdk.Msg) ([]string, error) {
switch msg := cosmosMsg.(type) {

case *govv1beta1.MsgSubmitProposal:
addresses := []string{msg.Proposer}

var content govv1beta1.Content
err := cdc.UnpackAny(msg.Content, &content)
if err != nil {
return nil, err
}

// Get addresses from contents
switch content := content.(type) {
//nolint:staticcheck // Let's keep this for the time being
case *distrtypes.CommunityPoolSpendProposal:
addresses = append(addresses, content.Recipient)
}

return addresses, nil

case *govv1beta1.MsgDeposit:
return []string{msg.Depositor}, nil

case *govv1beta1.MsgVote:
return []string{msg.Voter}, nil

}

return nil, MessageNotSupported(cosmosMsg)
}

// GovV1MessageParser returns the list of all the accounts involved in the given
// message if it's related to the x/gov v1 module
func GovV1MessageParser(cdc codec.Codec, cosmosMsg sdk.Msg) ([]string, error) {
switch msg := cosmosMsg.(type) {

case *govv1.MsgSubmitProposal:
addresses := []string{msg.Proposer}
return addresses, nil

case *govv1.MsgDeposit:
return []string{msg.Depositor}, nil

case *govv1.MsgVote:
return []string{msg.Voter}, nil

}

return nil, MessageNotSupported(cosmosMsg)
func DefaultMessagesParser(tx *types.Tx) ([]string, error) {
allAddressess := parseAddressesFromEvents(tx)
return allAddressess, nil
}

// IBCTransferMessagesParser returns the list of all the accounts involved in the given
// message if it's related to the x/iBCTransfer module
func IBCTransferMessagesParser(_ codec.Codec, cosmosMsg sdk.Msg) ([]string, error) {
switch msg := cosmosMsg.(type) {

case *ibctransfertypes.MsgTransfer:
return []string{msg.Sender, msg.Receiver}, nil

case *channeltypes.MsgRecvPacket:
var data ibctransfertypes.FungibleTokenPacketData
if err := ibctransfertypes.ModuleCdc.UnmarshalJSON(msg.Packet.Data, &data); err != nil {
// The packet data is not a FungibleTokenPacketData, so nothing to update
return nil, nil
// function to remove duplicate values
func removeDuplicates(s []string) []string {
bucket := make(map[string]bool)
var result []string
for _, str := range s {
if _, ok := bucket[str]; !ok {
bucket[str] = true
result = append(result, str)
}

// We are receiving some IBC tokens, so we need to update the receiver balance
// as well as the message signer (the relayer)
return []string{data.Receiver, msg.Signer}, nil
}

return nil, MessageNotSupported(cosmosMsg)
}

// SlashingMessagesParser returns the list of all the accounts involved in the given
// message if it's related to the x/slashing module
func SlashingMessagesParser(_ codec.Codec, cosmosMsg sdk.Msg) ([]string, error) {
switch msg := cosmosMsg.(type) {

case *slashingtypes.MsgUnjail:
return []string{msg.ValidatorAddr}, nil

}

return nil, MessageNotSupported(cosmosMsg)
return result
}

// StakingMessagesParser returns the list of all the accounts involved in the given
// message if it's related to the x/staking module
func StakingMessagesParser(_ codec.Codec, cosmosMsg sdk.Msg) ([]string, error) {
switch msg := cosmosMsg.(type) {

case *stakingtypes.MsgCreateValidator:
return []string{msg.ValidatorAddress, msg.DelegatorAddress}, nil
func parseAddressesFromEvents(tx *types.Tx) []string {
var allAddressess []string
chainPrefix := config.Cfg.Chain.Bech32Prefix

case *stakingtypes.MsgEditValidator:
return []string{msg.ValidatorAddress}, nil

case *stakingtypes.MsgDelegate:
return []string{msg.DelegatorAddress, msg.ValidatorAddress}, nil

case *stakingtypes.MsgBeginRedelegate:
return []string{msg.DelegatorAddress, msg.ValidatorSrcAddress, msg.ValidatorDstAddress}, nil

case *stakingtypes.MsgUndelegate:
return []string{msg.DelegatorAddress, msg.ValidatorAddress}, nil

}

return nil, MessageNotSupported(cosmosMsg)
}

// AuthzMessageParser returns the list of all the accounts involved in the given
// message if it's related to the x/authz module
func AuthzMessageParser(c codec.Codec, cosmosMsg sdk.Msg) ([]string, error) {
switch msg := cosmosMsg.(type) {
case *authztypes.MsgGrant:
return []string{msg.Grantee, msg.Granter}, nil
case *authztypes.MsgRevoke:
return []string{msg.Grantee, msg.Granter}, nil
case *authztypes.MsgExec:
for _, index := range msg.Msgs {
var executedMsg sdk.Msg
if err := c.UnpackAny(index, &executedMsg); err != nil {
return nil, fmt.Errorf("error while unpacking message from authz: %s", err)
for _, event := range tx.Events {
for _, attribute := range event.Attributes {
if strings.Contains(attribute.Value, "/") {
continue
}

switch {
case strings.Contains(index.TypeUrl, "bank"):
addresses, err := BankMessagesParser(c, executedMsg)
if err != nil {
return nil, MessageNotSupported(executedMsg)
}
addresses = append(addresses, msg.Grantee)
return addresses, nil
case strings.Contains(index.TypeUrl, "crisis"):
addresses, err := CrisisMessagesParser(c, executedMsg)
if err != nil {
return nil, MessageNotSupported(executedMsg)
}
addresses = append(addresses, msg.Grantee)
return addresses, nil
case strings.Contains(index.TypeUrl, "distribution"):
addresses, err := DistributionMessagesParser(c, executedMsg)
if err != nil {
return nil, MessageNotSupported(executedMsg)
}
addresses = append(addresses, msg.Grantee)
return addresses, nil
case strings.Contains(index.TypeUrl, "evidence"):
addresses, err := EvidenceMessagesParser(c, executedMsg)
if err != nil {
return nil, MessageNotSupported(executedMsg)
}
addresses = append(addresses, msg.Grantee)
return addresses, nil
case strings.Contains(index.TypeUrl, "gov.v1beta1"):
addresses, err := GovV1Beta1MessagesParser(c, executedMsg)
if err != nil {
return nil, MessageNotSupported(executedMsg)
}
addresses = append(addresses, msg.Grantee)
return addresses, nil
case strings.Contains(index.TypeUrl, "gov.v1."):
addresses, err := GovV1MessageParser(c, executedMsg)
if err != nil {
return nil, MessageNotSupported(executedMsg)
}
addresses = append(addresses, msg.Grantee)
return addresses, nil
case strings.Contains(index.TypeUrl, "ibc"):
addresses, err := IBCTransferMessagesParser(c, executedMsg)
if err != nil {
return nil, MessageNotSupported(executedMsg)
}
addresses = append(addresses, msg.Grantee)
return addresses, nil
case strings.Contains(index.TypeUrl, "slashing"):
addresses, err := SlashingMessagesParser(c, executedMsg)
if err != nil {
return nil, MessageNotSupported(executedMsg)
}
addresses = append(addresses, msg.Grantee)
return addresses, nil
case strings.Contains(index.TypeUrl, "staking"):
addresses, err := StakingMessagesParser(c, executedMsg)
if err != nil {
return nil, MessageNotSupported(executedMsg)
}
addresses = append(addresses, msg.Grantee)
return addresses, nil
default:
addresses, err := DefaultMessagesParser(c, executedMsg)
if err != nil {
return nil, MessageNotSupported(executedMsg)
}
addresses = append(addresses, msg.Grantee)
return addresses, nil
if strings.Contains(attribute.Value, chainPrefix) {
allAddressess = append(allAddressess, attribute.Value)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This won't work. If the attribute is, for example desmos-subspace-1 then it would be inserted as an address.

Instead, we should make sure the address is a valid format first. This can be done using sdk.AccAddressFromBech32 or the other methods (if we want to include validator addresses as well). Then, if the error of the parsing is not nill, we include the address inside the list of parsed addresses. Otherwise we skip it.

}
}

}
allInvolvedAddresses := removeDuplicates(allAddressess)

return nil, MessageNotSupported(cosmosMsg)
return allInvolvedAddresses
}
2 changes: 1 addition & 1 deletion modules/messages/message_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func HandleMsg(
) error {

// Get the involved addresses
addresses, err := parseAddresses(cdc, msg)
addresses, err := parseAddresses(tx)
if err != nil {
return err
}
Expand Down