diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d81f829..209f011e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,9 @@ ## Unreleased + +### Changes +- ([\#100](https://github.com/forbole/juno/pull/100)) improve account relationship mapping + +## v5.2.0 ### Changes - ([\#97](https://github.com/forbole/juno/pull/97)) Add `x/authz` message parser diff --git a/modules/messages/account_parser.go b/modules/messages/account_parser.go index edea5065..01cd453b 100644 --- a/modules/messages/account_parser.go +++ b/modules/messages/account_parser.go @@ -2,23 +2,11 @@ package messages import ( "fmt" - "strings" "github.com/gogo/protobuf/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/v3/modules/apps/transfer/types" - channeltypes "github.com/cosmos/ibc-go/v3/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 @@ -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(string(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 := 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(string(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 } diff --git a/modules/messages/message_handler.go b/modules/messages/message_handler.go index f0699409..a7f94a9d 100644 --- a/modules/messages/message_handler.go +++ b/modules/messages/message_handler.go @@ -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 }