diff --git a/app/app.go b/app/app.go index 98ff6d38c..cc3ace67f 100644 --- a/app/app.go +++ b/app/app.go @@ -536,11 +536,13 @@ func NewBandApp( app.MintKeeper, app.DistrKeeper, app.GovKeeper, + app.GroupKeeper, app.OracleKeeper, app.ICAHostKeeper, app.IBCKeeper.ClientKeeper, app.IBCKeeper.ConnectionKeeper, app.IBCKeeper.ChannelKeeper, + keys[group.StoreKey], emitterFlag, false, )) diff --git a/flusher/flusher/handler.py b/flusher/flusher/handler.py index 4478d57e9..bb74c62fc 100644 --- a/flusher/flusher/handler.py +++ b/flusher/flusher/handler.py @@ -38,6 +38,11 @@ counterparty_chains, connections, channels, + groups, + group_members, + group_policies, + group_proposals, + group_votes, ) @@ -81,6 +86,9 @@ def get_data_source_id(self, id): def get_oracle_script_id(self, id): return self.conn.execute(select([oracle_scripts.c.id]).where(oracle_scripts.c.id == id)).scalar() + def get_group_id_from_policy_address(self, address): + return self.conn.execute(select([group_policies.c.group_id]).where(group_policies.c.address == address)).scalar() + def handle_new_block(self, msg): self.conn.execute(blocks.insert(), msg) @@ -128,6 +136,47 @@ def handle_new_data_source(self, msg): self.conn.execute(data_sources.insert(), msg) self.init_data_source_request_count(msg["id"]) + def handle_new_group(self, msg): + self.conn.execute(groups.insert(), msg) + + def handle_new_group_member(self, msg): + msg["account_id"] = self.get_account_id(msg["address"]) + del msg["address"] + self.conn.execute(group_members.insert(), msg) + + def handle_new_group_policy(self, msg): + self.get_account_id(msg["address"]) + self.conn.execute(group_policies.insert(), msg) + + def handle_new_group_proposal(self, msg): + msg["group_id"] = self.get_group_id_from_policy_address(msg["group_policy_address"]) + self.conn.execute(group_proposals.insert(), msg) + + def handle_new_group_vote(self, msg): + msg["voter_id"] = self.get_account_id(msg["voter_address"]) + del msg["voter_address"] + self.conn.execute(group_votes.insert(), msg) + + def handle_update_group(self, msg): + self.conn.execute(groups.update().where(groups.c.id == msg["id"]).values(**msg)) + + def handle_remove_group_member(self, msg): + account_id = self.get_account_id(msg["address"]) + self.conn.execute(group_members.delete().where((group_members.c.group_id == msg["group_id"]) & (group_members.c.account_id == account_id))) + + def handle_remove_group_members_by_group_id(self, msg): + self.conn.execute(group_members.delete().where(group_members.c.group_id == msg["group_id"])) + + def handle_update_group_policy(self, msg): + self.conn.execute(group_policies.update().where(group_policies.c.address == msg["address"]).values(**msg)) + + def handle_update_group_proposal(self, msg): + msg["group_id"] = self.get_group_id_from_policy_address(msg["group_policy_address"]) + self.conn.execute(group_proposals.update().where(group_proposals.c.id == msg["id"]).values(**msg)) + + def handle_update_group_proposal_by_id(self, msg): + self.conn.execute(group_proposals.update().where(group_proposals.c.id == msg["id"]).values(**msg)) + def handle_set_data_source(self, msg): msg["transaction_id"] = self.get_transaction_id(msg["tx_hash"]) del msg["tx_hash"] diff --git a/hooks/emitter/emitter.go b/hooks/emitter/emitter.go index 3f385ada6..dcd17415d 100644 --- a/hooks/emitter/emitter.go +++ b/hooks/emitter/emitter.go @@ -10,6 +10,7 @@ import ( "github.com/cometbft/cometbft/crypto/tmhash" tmjson "github.com/cometbft/cometbft/libs/json" "github.com/cosmos/cosmos-sdk/codec" + storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" "github.com/cosmos/cosmos-sdk/x/authz" @@ -21,6 +22,7 @@ import ( govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + groupkeeper "github.com/cosmos/cosmos-sdk/x/group/keeper" mintkeeper "github.com/cosmos/cosmos-sdk/x/mint/keeper" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" @@ -57,6 +59,7 @@ type Hook struct { mintKeeper mintkeeper.Keeper distrKeeper distrkeeper.Keeper govKeeper govkeeper.Keeper + groupKeeper groupkeeper.Keeper oracleKeeper oraclekeeper.Keeper icahostKeeper icahostkeeper.Keeper @@ -64,6 +67,8 @@ type Hook struct { clientkeeper clientkeeper.Keeper connectionkeeper connectionkeeper.Keeper channelkeeper channelkeeper.Keeper + + groupStoreKey storetypes.StoreKey } // NewHook creates an emitter hook instance that will be added in Band App. @@ -77,11 +82,13 @@ func NewHook( mintKeeper mintkeeper.Keeper, distrKeeper distrkeeper.Keeper, govKeeper govkeeper.Keeper, + groupKeeper groupkeeper.Keeper, oracleKeeper keeper.Keeper, icahostKeeper icahostkeeper.Keeper, clientkeeper clientkeeper.Keeper, connectionkeeper connectionkeeper.Keeper, channelkeeper channelkeeper.Keeper, + groupstorekey storetypes.StoreKey, kafkaURI string, emitStartState bool, ) *Hook { @@ -103,11 +110,13 @@ func NewHook( mintKeeper: mintKeeper, distrKeeper: distrKeeper, govKeeper: govKeeper, + groupKeeper: groupKeeper, oracleKeeper: oracleKeeper, icahostKeeper: icahostKeeper, clientkeeper: clientkeeper, connectionkeeper: connectionkeeper, channelkeeper: channelkeeper, + groupStoreKey: groupstorekey, emitStartState: emitStartState, } } diff --git a/hooks/emitter/group.go b/hooks/emitter/group.go new file mode 100644 index 000000000..6a726f8fe --- /dev/null +++ b/hooks/emitter/group.go @@ -0,0 +1,392 @@ +package emitter + +import ( + "encoding/json" + "strings" + + "github.com/bandprotocol/chain/v2/hooks/common" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" + "github.com/cosmos/cosmos-sdk/x/group" + proto "github.com/cosmos/gogoproto/proto" +) + +// handleGroupMsgCreateGroup implements emitter handler for Group's MsgCreateGroup. +func (h *Hook) handleGroupMsgCreateGroup( + ctx sdk.Context, evMap common.EvMap, +) { + groupId := uint64( + common.Atoi(strings.Trim(evMap[proto.MessageName(&group.EventCreateGroup{})+".group_id"][0], `"`)), + ) + groupInfoResponse, _ := h.groupKeeper.GroupInfo( + sdk.WrapSDKContext(ctx), + &group.QueryGroupInfoRequest{GroupId: groupId}, + ) + groupInfo := groupInfoResponse.Info + h.Write("NEW_GROUP", common.JsDict{ + "id": groupId, + "version": groupInfo.Version, + "admin": groupInfo.Admin, + "metadata": groupInfo.Metadata, + "total_weight": groupInfo.TotalWeight, + "created_at": common.TimeToNano(&groupInfo.CreatedAt), + }) + h.doAddGroupMembers(ctx, groupId) +} + +// handleGroupMsgCreateGroup implements emitter handler for Group's MsgCreateGroupPolicy. +func (h *Hook) handleGroupMsgCreateGroupPolicy( + ctx sdk.Context, evMap common.EvMap, +) { + policyAddress := strings.Trim(evMap[proto.MessageName(&group.EventCreateGroupPolicy{})+".address"][0], `"`) + groupPolicyResponse, _ := h.groupKeeper.GroupPolicyInfo( + sdk.WrapSDKContext(ctx), + &group.QueryGroupPolicyInfoRequest{ + Address: policyAddress, + }, + ) + groupPolicyInfo := groupPolicyResponse.Info + decisionPolicy, _ := groupPolicyInfo.GetDecisionPolicy() + h.Write("NEW_GROUP_POLICY", common.JsDict{ + "address": policyAddress, + "group_id": groupPolicyInfo.GroupId, + "admin": groupPolicyInfo.Admin, + "metadata": groupPolicyInfo.Metadata, + "version": groupPolicyInfo.Version, + "decision_policy": decisionPolicy, + "created_at": common.TimeToNano(&groupPolicyInfo.CreatedAt), + }) +} + +// handleGroupMsgCreateGroupWithPolicy implements emitter handler for Group's MsgCreateGroupWithPolicy. +func (h *Hook) handleGroupMsgCreateGroupWithPolicy( + ctx sdk.Context, evMap common.EvMap, +) { + h.handleGroupMsgCreateGroup(ctx, evMap) + h.handleGroupMsgCreateGroupPolicy(ctx, evMap) +} + +// handleGroupMsgSubmitProposal implements emitter handler for Group's MsgSubmitProposal. +func (h *Hook) handleGroupMsgSubmitProposal( + ctx sdk.Context, evMap common.EvMap, +) { + proposalId := uint64( + common.Atoi(strings.Trim(evMap[proto.MessageName(&group.EventSubmitProposal{})+".proposal_id"][0], `"`)), + ) + proposalResponse, _ := h.groupKeeper.Proposal( + sdk.WrapSDKContext(ctx), + &group.QueryProposalRequest{ProposalId: proposalId}, + ) + proposal := proposalResponse.Proposal + msgs, _ := proposal.GetMsgs() + messages := make([]common.JsDict, len(msgs)) + for i, m := range msgs { + messages[i] = common.JsDict{ + "msg": m, + "type": sdk.MsgTypeURL(m), + } + } + + h.Write("NEW_GROUP_PROPOSAL", common.JsDict{ + "id": proposal.Id, + "group_policy_address": proposal.GroupPolicyAddress, + "metadata": proposal.Metadata, + "proposers": strings.Join(proposal.Proposers, ","), + "submit_time": common.TimeToNano(&proposal.SubmitTime), + "group_version": proposal.GroupVersion, + "group_policy_version": proposal.GroupPolicyVersion, + "status": proposal.Status.String(), + "yes_vote": proposal.FinalTallyResult.YesCount, + "no_vote": proposal.FinalTallyResult.NoCount, + "no_with_veto_vote": proposal.FinalTallyResult.NoWithVetoCount, + "abstain_vote": proposal.FinalTallyResult.AbstainCount, + "voting_period_end": common.TimeToNano(&proposal.VotingPeriodEnd), + "executor_result": proposal.ExecutorResult.String(), + "messages": messages, + "title": proposal.Title, + "summary": proposal.Summary, + }) +} + +// handleGroupMsgVote implements emitter handler for Group's MsgVote. +func (h *Hook) handleGroupMsgVote( + ctx sdk.Context, msg *group.MsgVote, evMap common.EvMap, +) { + proposalId := uint64( + common.Atoi(strings.Trim(evMap[proto.MessageName(&group.EventVote{})+".proposal_id"][0], `"`)), + ) + voteResponse, err := h.groupKeeper.VoteByProposalVoter( + sdk.WrapSDKContext(ctx), + &group.QueryVoteByProposalVoterRequest{ + ProposalId: proposalId, + Voter: msg.Voter, + }, + ) + if err != nil { + return + } + vote := voteResponse.Vote + h.Write("NEW_GROUP_VOTE", common.JsDict{ + "group_proposal_id": proposalId, + "voter_address": vote.Voter, + "option": vote.Option.String(), + "metadata": vote.Metadata, + "submit_time": common.TimeToNano(&vote.SubmitTime), + }) +} + +// handleGroupMsgLeaveGroup implements emitter handler for Group's MsgLeaveGroup. +func (h *Hook) handleGroupMsgLeaveGroup( + ctx sdk.Context, evMap common.EvMap, +) { + groupId := uint64( + common.Atoi(strings.Trim(evMap[proto.MessageName(&group.EventLeaveGroup{})+".group_id"][0], `"`)), + ) + address := strings.Trim(evMap[proto.MessageName(&group.EventLeaveGroup{})+".address"][0], `"`) + h.doUpdateGroup(ctx, groupId) + h.Write("REMOVE_GROUP_MEMBER", common.JsDict{ + "group_id": groupId, + "address": address, + }) +} + +// handleGroupMsgUpdateGroupAdmin implements emitter handler for Group's MsgUpdateGroupAdmin. +func (h *Hook) handleGroupMsgUpdateGroupAdmin( + ctx sdk.Context, evMap common.EvMap, +) { + groupId := uint64( + common.Atoi(strings.Trim(evMap[proto.MessageName(&group.EventUpdateGroup{})+".group_id"][0], `"`)), + ) + h.doUpdateGroup(ctx, groupId) +} + +// handleGroupMsgUpdateGroupMembers implements emitter handler for Group's MsgUpdateGroupMembers. +func (h *Hook) handleGroupMsgUpdateGroupMembers( + ctx sdk.Context, msg *group.MsgUpdateGroupMembers, evMap common.EvMap, +) { + h.Write("REMOVE_GROUP_MEMBERS_BY_GROUP_ID", common.JsDict{ + "group_id": msg.GroupId, + }) + h.doAddGroupMembers(ctx, msg.GroupId) + h.doUpdateGroup(ctx, msg.GroupId) +} + +// handleGroupMsgUpdateGroupMetadata implements emitter handler for Group's MsgUpdateGroupMetadata. +func (h *Hook) handleGroupMsgUpdateGroupMetadata( + ctx sdk.Context, evMap common.EvMap, +) { + groupId := uint64( + common.Atoi(strings.Trim(evMap[proto.MessageName(&group.EventUpdateGroup{})+".group_id"][0], `"`)), + ) + h.doUpdateGroup(ctx, groupId) +} + +// handleGroupMsgUpdateGroupPolicyAdmin implements emitter handler for Group's MsgUpdateGroupPolicyAdmin. +func (h *Hook) handleGroupMsgUpdateGroupPolicyAdmin( + ctx sdk.Context, evMap common.EvMap, +) { + groupPolicyAddress := strings.Trim(evMap[proto.MessageName(&group.EventUpdateGroupPolicy{})+".address"][0], `"`) + h.doUpdateGroupPolicy(ctx, groupPolicyAddress) +} + +// handleGroupMsgUpdateGroupPolicyDecisionPolicy implements emitter handler for Group's MsgUpdateGroupPolicyDecisionPolicy. +func (h *Hook) handleGroupMsgUpdateGroupPolicyDecisionPolicy( + ctx sdk.Context, evMap common.EvMap, +) { + groupPolicyAddress := strings.Trim(evMap[proto.MessageName(&group.EventUpdateGroupPolicy{})+".address"][0], `"`) + h.doUpdateGroupPolicy(ctx, groupPolicyAddress) +} + +// handleGroupMsgUpdateGroupPolicyMetadata implements emitter handler for Group's MsgUpdateGroupPolicyMetadata. +func (h *Hook) handleGroupMsgUpdateGroupPolicyMetadata( + ctx sdk.Context, evMap common.EvMap, +) { + groupPolicyAddress := strings.Trim(evMap[proto.MessageName(&group.EventUpdateGroupPolicy{})+".address"][0], `"`) + h.doUpdateGroupPolicy(ctx, groupPolicyAddress) +} + +// handleGroupMsgWithdrawProposal implements emitter handler for Group's MsgWithdrawProposal. +func (h *Hook) handleGroupMsgWithdrawProposal( + ctx sdk.Context, evMap common.EvMap, +) { + proposalId := uint64( + common.Atoi(strings.Trim(evMap[proto.MessageName(&group.EventWithdrawProposal{})+".proposal_id"][0], `"`)), + ) + h.doUpdateGroupProposal(ctx, proposalId) +} + +// handleGroupEventExec implements emitter handler for Group's EventExec. +func (h *Hook) handleGroupEventExec( + ctx sdk.Context, evMap common.EvMap, +) { + if len(evMap[proto.MessageName(&group.EventExec{})+".proposal_id"]) == 0 { + return + } + proposalId := uint64( + common.Atoi(strings.Trim(evMap[proto.MessageName(&group.EventExec{})+".proposal_id"][0], `"`)), + ) + executorResult := strings.Trim(evMap[proto.MessageName(&group.EventExec{})+".result"][0], `"`) + h.Write("UPDATE_GROUP_PROPOSAL_BY_ID", common.JsDict{ + "id": proposalId, + "executor_result": executorResult, + }) + + h.handleGroupEventProposalPruned(ctx, evMap) +} + +// handleGroupEventProposalPruned implements emitter handler for Group's EventProposalPruned. +func (h *Hook) handleGroupEventProposalPruned( + ctx sdk.Context, evMap common.EvMap, +) { + if len(evMap[proto.MessageName(&group.EventProposalPruned{})+".proposal_id"]) == 0 { + return + } + proposalId := uint64( + common.Atoi(strings.Trim(evMap[proto.MessageName(&group.EventProposalPruned{})+".proposal_id"][0], `"`)), + ) + proposalStatus := strings.Trim(evMap[proto.MessageName(&group.EventProposalPruned{})+".status"][0], `"`) + var tallyResult group.TallyResult + json.Unmarshal([]byte(evMap[proto.MessageName(&group.EventProposalPruned{})+".tally_result"][0]), &tallyResult) + h.Write("UPDATE_GROUP_PROPOSAL_BY_ID", common.JsDict{ + "id": proposalId, + "status": proposalStatus, + "yes_vote": tallyResult.YesCount, + "no_vote": tallyResult.NoCount, + "no_with_veto_vote": tallyResult.NoWithVetoCount, + "abstain_vote": tallyResult.AbstainCount, + }) +} + +func (h *Hook) doUpdateGroup(ctx sdk.Context, groupId uint64) { + groupInfoResponse, _ := h.groupKeeper.GroupInfo( + sdk.WrapSDKContext(ctx), + &group.QueryGroupInfoRequest{GroupId: groupId}, + ) + groupInfo := groupInfoResponse.Info + h.Write("UPDATE_GROUP", common.JsDict{ + "id": groupId, + "version": groupInfo.Version, + "admin": groupInfo.Admin, + "metadata": groupInfo.Metadata, + "total_weight": groupInfo.TotalWeight, + "created_at": common.TimeToNano(&groupInfo.CreatedAt), + }) +} + +func (h *Hook) doUpdateGroupPolicy(ctx sdk.Context, policyAddress string) { + groupPolicyResponse, _ := h.groupKeeper.GroupPolicyInfo( + sdk.WrapSDKContext(ctx), + &group.QueryGroupPolicyInfoRequest{ + Address: policyAddress, + }, + ) + groupPolicyInfo := groupPolicyResponse.Info + decisionPolicy, _ := groupPolicyInfo.GetDecisionPolicy() + h.Write("UPDATE_GROUP_POLICY", common.JsDict{ + "address": policyAddress, + "group_id": groupPolicyInfo.GroupId, + "admin": groupPolicyInfo.Admin, + "metadata": groupPolicyInfo.Metadata, + "version": groupPolicyInfo.Version, + "decision_policy": decisionPolicy, + "created_at": common.TimeToNano(&groupPolicyInfo.CreatedAt), + }) + + h.doAbortProposals(ctx, policyAddress) +} + +func (h *Hook) doAbortProposals(ctx sdk.Context, policyAddress string) { + groupProposalsResponse, _ := h.groupKeeper.ProposalsByGroupPolicy( + sdk.WrapSDKContext(ctx), + &group.QueryProposalsByGroupPolicyRequest{ + Address: policyAddress, + }, + ) + for { + groupProposals := groupProposalsResponse.Proposals + for _, groupProposal := range groupProposals { + if groupProposal.Status == group.PROPOSAL_STATUS_ABORTED { + h.doUpdateGroupProposal(ctx, groupProposal.Id) + } + } + if len(groupProposalsResponse.Pagination.NextKey) == 0 { + break + } + groupProposalsResponse, _ = h.groupKeeper.ProposalsByGroupPolicy( + sdk.WrapSDKContext(ctx), + &group.QueryProposalsByGroupPolicyRequest{ + Address: policyAddress, + Pagination: &query.PageRequest{ + Key: groupProposalsResponse.Pagination.NextKey, + }, + }, + ) + } +} + +func (h *Hook) doUpdateGroupProposal(ctx sdk.Context, proposalId uint64) { + proposalResponse, _ := h.groupKeeper.Proposal( + sdk.WrapSDKContext(ctx), + &group.QueryProposalRequest{ProposalId: proposalId}, + ) + proposal := proposalResponse.Proposal + msgs, _ := proposal.GetMsgs() + messages := make([]common.JsDict, len(msgs)) + for i, m := range msgs { + messages[i] = common.JsDict{ + "msg": m, + "type": sdk.MsgTypeURL(m), + } + } + + h.Write("UPDATE_GROUP_PROPOSAL", common.JsDict{ + "id": proposal.Id, + "group_policy_address": proposal.GroupPolicyAddress, + "metadata": proposal.Metadata, + "proposers": strings.Join(proposal.Proposers, ","), + "submit_time": common.TimeToNano(&proposal.SubmitTime), + "group_version": proposal.GroupVersion, + "group_policy_version": proposal.GroupPolicyVersion, + "status": proposal.Status.String(), + "yes_vote": proposal.FinalTallyResult.YesCount, + "no_vote": proposal.FinalTallyResult.NoCount, + "no_with_veto_vote": proposal.FinalTallyResult.NoWithVetoCount, + "abstain_vote": proposal.FinalTallyResult.AbstainCount, + "voting_period_end": common.TimeToNano(&proposal.VotingPeriodEnd), + "executor_result": proposal.ExecutorResult.String(), + "messages": messages, + "title": proposal.Title, + "summary": proposal.Summary, + }) +} + +func (h *Hook) doAddGroupMembers(ctx sdk.Context, groupId uint64) { + groupMembersResponse, _ := h.groupKeeper.GroupMembers( + sdk.WrapSDKContext(ctx), + &group.QueryGroupMembersRequest{GroupId: groupId}, + ) + for { + groupMembers := groupMembersResponse.Members + for _, groupMember := range groupMembers { + h.Write("NEW_GROUP_MEMBER", common.JsDict{ + "group_id": groupId, + "address": groupMember.Member.Address, + "weight": groupMember.Member.Weight, + "metadata": groupMember.Member.Metadata, + "added_at": common.TimeToNano(&groupMember.Member.AddedAt), + }) + } + if len(groupMembersResponse.Pagination.NextKey) == 0 { + break + } + groupMembersResponse, _ = h.groupKeeper.GroupMembers( + sdk.WrapSDKContext(ctx), + &group.QueryGroupMembersRequest{ + GroupId: groupId, + Pagination: &query.PageRequest{ + Key: groupMembersResponse.Pagination.NextKey, + }, + }, + ) + } +} diff --git a/hooks/emitter/handler.go b/hooks/emitter/handler.go index 28cf31f1a..106e09394 100644 --- a/hooks/emitter/handler.go +++ b/hooks/emitter/handler.go @@ -1,16 +1,22 @@ package emitter import ( + "time" + abci "github.com/cometbft/cometbft/abci/types" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/kv" "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" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + "github.com/cosmos/cosmos-sdk/x/group" + groupkeeper "github.com/cosmos/cosmos-sdk/x/group/keeper" slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + proto "github.com/cosmos/gogoproto/proto" transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" @@ -118,12 +124,53 @@ func (h *Hook) handleMsg(ctx sdk.Context, txHash []byte, msg sdk.Msg, log sdk.AB h.handleMsgRevoke(msg, detail) case *authz.MsgExec: h.handleMsgExec(ctx, txHash, msg, log, detail) + case *group.MsgCreateGroup: + h.handleGroupMsgCreateGroup(ctx, evMap) + case *group.MsgCreateGroupPolicy: + h.handleGroupMsgCreateGroupPolicy(ctx, evMap) + case *group.MsgCreateGroupWithPolicy: + h.handleGroupMsgCreateGroupWithPolicy(ctx, evMap) + case *group.MsgExec: + h.handleGroupEventExec(ctx, evMap) + case *group.MsgLeaveGroup: + h.handleGroupMsgLeaveGroup(ctx, evMap) + case *group.MsgSubmitProposal: + h.handleGroupMsgSubmitProposal(ctx, evMap) + case *group.MsgUpdateGroupAdmin: + h.handleGroupMsgUpdateGroupAdmin(ctx, evMap) + case *group.MsgUpdateGroupMembers: + h.handleGroupMsgUpdateGroupMembers(ctx, msg, evMap) + case *group.MsgUpdateGroupMetadata: + h.handleGroupMsgUpdateGroupMetadata(ctx, evMap) + case *group.MsgUpdateGroupPolicyAdmin: + h.handleGroupMsgUpdateGroupPolicyAdmin(ctx, evMap) + case *group.MsgUpdateGroupPolicyDecisionPolicy: + h.handleGroupMsgUpdateGroupPolicyDecisionPolicy(ctx, evMap) + case *group.MsgUpdateGroupPolicyMetadata: + h.handleGroupMsgUpdateGroupPolicyMetadata(ctx, evMap) + case *group.MsgVote: + h.handleGroupMsgVote(ctx, msg, evMap) + h.handleGroupEventExec(ctx, evMap) + case *group.MsgWithdrawProposal: + h.handleGroupMsgWithdrawProposal(ctx, evMap) default: break } } func (h *Hook) handleBeginBlockEndBlockEvent(ctx sdk.Context, event abci.Event) { + timeBytes := sdk.FormatTimeBytes(ctx.BlockTime().UTC()) + lenTimeByte := byte(len(timeBytes)) + prefix := []byte{groupkeeper.ProposalsByVotingPeriodEndPrefix} + + iterator := ctx.KVStore(h.groupStoreKey). + Iterator(prefix, sdk.PrefixEndBytes(append(append(prefix, lenTimeByte), timeBytes...))) + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + proposalID, _ := splitKeyWithTime(iterator.Key()) + h.doUpdateGroupProposal(ctx, proposalID) + } + events := sdk.StringifyEvents([]abci.Event{event}) evMap := parseEvents(events) switch event.Type { @@ -145,7 +192,22 @@ func (h *Hook) handleBeginBlockEndBlockEvent(ctx sdk.Context, event abci.Event) h.handleEventTypeTransfer(evMap) case channeltypes.EventTypeSendPacket: h.handleEventSendPacket(ctx, evMap) + case proto.MessageName(&group.EventProposalPruned{}): + h.handleGroupEventProposalPruned(ctx, evMap) default: break } } + +func splitKeyWithTime(key []byte) (proposalID uint64, endTime time.Time) { + var lenTime = len(sdk.FormatTimeBytes(time.Now())) + kv.AssertKeyLength(key[2:], 8+lenTime) + + endTime, err := sdk.ParseTimeBytes(key[2 : 2+lenTime]) + if err != nil { + panic(err) + } + + proposalID = sdk.BigEndianToUint64(key[2+lenTime:]) + return +}