Skip to content

Commit

Permalink
feat: improve account relationship mapping (forbole#100)
Browse files Browse the repository at this point in the history
<!-- < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < ☺
v                               ✰  Thanks for creating a PR! ✰    
v    Before smashing the submit button please review the checkboxes.
v If a checkbox is n/a - please still include it but + a little note why
☺ > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > >  -->

## Description
<!-- Small description -->
[BDU-950](https://forbole.atlassian.net/browse/BDU-950) &
[BDU-1006](https://forbole.atlassian.net/browse/BDU-1006)

## Checklist
- [x] Targeted PR against correct branch.
- [x] Linked to Github issue with discussion and accepted design OR link
to spec that describes this work.
- [ ] Wrote unit tests.  
- [x] Re-reviewed `Files changed` in the Github PR explorer.


[BDU-950]:
https://forbole.atlassian.net/browse/BDU-950?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

[BDU-1006]:
https://forbole.atlassian.net/browse/BDU-1006?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
  • Loading branch information
MonikaCat authored and dzmitryhil committed Dec 16, 2024
1 parent d3d3010 commit 4a5071e
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 315 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## Unreleased

### Changes
- ([\#100](https://github.com/forbole/juno/pull/100)) improve account relationship mapping

## v5.2.0
### Changes
- Exposed `Database#CreatePartitionIfNotExists` method
Expand Down
347 changes: 33 additions & 314 deletions modules/messages/account_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,11 @@ package messages

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"
)

// MessageNotSupported returns an error telling that the given message is not supported
Expand All @@ -29,323 +16,55 @@ 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
func DefaultMessagesParser(tx *types.Tx) ([]string, error) {
allAddressess := parseAddressesFromEvents(tx)
return allAddressess, 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)
// 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)
}
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)
return result
}

// 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
func parseAddressesFromEvents(tx *types.Tx) []string {
var allAddressess []string

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)
}

// 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
}

// 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)
}

// 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

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 {
// check if event value is validator address
valAddresss, _ := sdk.ValAddressFromBech32(attribute.Value)
if valAddresss != nil {
allAddressess = append(allAddressess, valAddresss.String())
}

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
// check if event value is sdk address
sdkAddress, err := sdk.AccAddressFromBech32(attribute.Value)
if err != nil {
// skip if value is not sdk address
continue
}

allAddressess = append(allAddressess, sdkAddress.String())
}

}
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

0 comments on commit 4a5071e

Please sign in to comment.