Skip to content

Commit

Permalink
feat: improve account relationship mapping (#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
☺ > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > >  -->

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

- [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 committed Dec 27, 2023
1 parent 67e8053 commit f43f4dc
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 286 deletions.
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
## Unreleased

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

## v5.2.0
### Changes
- Exposed `Database#CreatePartitionIfNotExists` method
- ([\#97](https://github.com/forbole/juno/pull/97)) Add `x/authz` message parser

## v5.1.0
### Changes
- Bumped Go version to `1.20`
- Fixed a bug that caused messages type urls to be empty in the database
- Implemented a better default `EncodingConfigBuilder`
- Added the ability to set `DatabaseConfig` field values using setter methods
- ([\#96](https://github.com/forbole/juno/pull/96)) Export `Codec` and `LegacyAmino` from the `Database` implementation

## v5.0.0
### Changes
- Updated Cosmos SDK to `v0.47.2`

### Database
- ([\#96](https://github.com/forbole/juno/pull/96)) Exported Codec and LegacyAmino codec inside Database wrapper

## v4.2.0
### Changes
- ([\#93](https://github.com/forbole/juno/pull/93)) Decode IBC transfer data to JSON for `/ibc.core.channel.v1.MsgRecvPacket` message
Expand Down
319 changes: 34 additions & 285 deletions modules/messages/account_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,11 @@ package messages

import (
"fmt"
"strings"

"github.com/gogo/protobuf/proto"
"github.com/cosmos/gogoproto/proto"

crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types"
evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types"
ibctransfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"
channeltypes "github.com/cosmos/ibc-go/v4/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/v4/types"
)

// MessageNotSupported returns an error telling that the given message is not supported
Expand All @@ -28,294 +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,
GovMessagesParser,
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)
}
for _, o := range msg.Outputs {
addresses = append(addresses, o.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)
}
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
}

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

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

var content govtypes.Content
err := cdc.UnpackAny(msg.Content, &content)
if err != nil {
return nil, err
}
func parseAddressesFromEvents(tx *types.Tx) []string {
var allAddressess []string

// Get addresses from contents
switch content := content.(type) {
case *distrtypes.CommunityPoolSpendProposal:
addresses = append(addresses, content.Recipient)
}

return addresses, nil

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

case *govtypes.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)

Check failure on line 51 in modules/messages/account_parser.go

View workflow job for this annotation

GitHub Actions / Unit-tests

cannot use attribute.Value (variable of type []byte) as string value in argument to sdk.ValAddressFromBech32
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"):
addresses, err := GovMessagesParser(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)

Check failure on line 57 in modules/messages/account_parser.go

View workflow job for this annotation

GitHub Actions / Unit-tests

cannot use attribute.Value (variable of type []byte) as string value in argument to sdk.AccAddressFromBech32
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 f43f4dc

Please sign in to comment.