Skip to content

Commit

Permalink
MAT-1397 - sidechannel implementation for slashing
Browse files Browse the repository at this point in the history
  • Loading branch information
mankenavenkatesh committed May 12, 2020
1 parent 23f15d9 commit 793c46c
Show file tree
Hide file tree
Showing 7 changed files with 552 additions and 37 deletions.
7 changes: 6 additions & 1 deletion bridge/setu/processor/slashing.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ func (sp *SlashingProcessor) sendTickAckToHeimdall(eventName string, logBytes st
"totalSlashedAmount", event.Amount,
"txHash", hmTypes.BytesToHeimdallHash(vLog.TxHash.Bytes()),
"logIndex", uint64(vLog.Index),
"blockNumber", vLog.BlockNumber,
)
return nil
}
Expand All @@ -191,12 +192,13 @@ func (sp *SlashingProcessor) sendTickAckToHeimdall(eventName string, logBytes st
"totalSlashedAmount", event.Amount,
"txHash", hmTypes.BytesToHeimdallHash(vLog.TxHash.Bytes()),
"logIndex", uint64(vLog.Index),
"blockNumber", vLog.BlockNumber,
)

// TODO - check if i am the proposer of this tick ack or not.

// create msg checkpoint ack message
msg := slashingTypes.NewMsgTickAck(helper.GetFromAddress(sp.cliCtx), hmTypes.BytesToHeimdallHash(vLog.TxHash.Bytes()), uint64(vLog.Index))
msg := slashingTypes.NewMsgTickAck(helper.GetFromAddress(sp.cliCtx), event.Amount, hmTypes.BytesToHeimdallHash(vLog.TxHash.Bytes()), uint64(vLog.Index), vLog.BlockNumber)

// return broadcast to heimdall
if err := sp.txBroadcaster.BroadcastToHeimdall(msg); err != nil {
Expand Down Expand Up @@ -228,6 +230,7 @@ func (sp *SlashingProcessor) sendUnjailToHeimdall(eventName string, logBytes str
"ValidatorID", event.ValidatorId,
"txHash", hmTypes.BytesToHeimdallHash(vLog.TxHash.Bytes()),
"logIndex", uint64(vLog.Index),
"blockNumber", vLog.BlockNumber,
)
return nil
}
Expand All @@ -237,6 +240,7 @@ func (sp *SlashingProcessor) sendUnjailToHeimdall(eventName string, logBytes str
"ValidatorID", event.ValidatorId,
"txHash", hmTypes.BytesToHeimdallHash(vLog.TxHash.Bytes()),
"logIndex", uint64(vLog.Index),
"blockNumber", vLog.BlockNumber,
)

// TODO - check if i am the proposer of unjail or not.
Expand All @@ -247,6 +251,7 @@ func (sp *SlashingProcessor) sendUnjailToHeimdall(eventName string, logBytes str
event.ValidatorId.Uint64(),
hmTypes.BytesToHeimdallHash(vLog.TxHash.Bytes()),
uint64(vLog.Index),
vLog.BlockNumber,
)

// return broadcast to heimdall
Expand Down
2 changes: 2 additions & 0 deletions slashing/client/cli/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@ const (
FlagValidatorID = "id"
FlagTxHash = "tx-hash"
FlagLogIndex = "log-index"
FlagAmount = "slashed-amount"
FlagSlashInfoHash = "slashinfo-hash"
FlagBlockNumber = "block-number"
)
24 changes: 23 additions & 1 deletion slashing/client/cli/tx.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package cli

import (
"errors"
"fmt"
"math/big"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
Expand All @@ -16,6 +18,8 @@ import (
hmTypes "github.com/maticnetwork/heimdall/types"
)

var logger = helper.Logger.With("module", "staking/client/cli")

func GetTxCmd(cdc *codec.Codec) *cobra.Command {
slashingTxCmd := &cobra.Command{
Use: types.ModuleName,
Expand Down Expand Up @@ -67,6 +71,7 @@ $ <appcli> tx slashing unjail --from mykey
uint64(validator),
hmTypes.HexToHeimdallHash(txHash),
uint64(viper.GetInt64(FlagLogIndex)),
viper.GetUint64(FlagBlockNumber),
)

// broadcast messages
Expand All @@ -75,9 +80,12 @@ $ <appcli> tx slashing unjail --from mykey
}
cmd.Flags().StringP(FlagProposerAddress, "p", "", "--proposer=<proposer-address>")
cmd.Flags().String(FlagTxHash, "", "--tx-hash=<transaction-hash>")
cmd.Flags().Uint64(FlagBlockNumber, 0, "--block-number=<block-number>")
cmd.MarkFlagRequired(FlagProposerAddress)
cmd.MarkFlagRequired(FlagTxHash)

if err := cmd.MarkFlagRequired(FlagBlockNumber); err != nil {
logger.Error("SendValidatorJoinTx | MarkFlagRequired | FlagBlockNumber", "Error", err)
}
return cmd
}

Expand Down Expand Up @@ -139,10 +147,17 @@ func GetCmdTickAck(cdc *codec.Codec) *cobra.Command {
return fmt.Errorf("transaction hash is required")
}

amount, ok := big.NewInt(0).SetString(viper.GetString(FlagAmount), 10)
if !ok {
return errors.New("Invalid stake amount")
}

msg := types.NewMsgTickAck(
proposer,
amount,
hmTypes.HexToHeimdallHash(txHash),
uint64(viper.GetInt64(FlagLogIndex)),
viper.GetUint64(FlagBlockNumber),
)

// broadcast messages
Expand All @@ -152,10 +167,17 @@ func GetCmdTickAck(cdc *codec.Codec) *cobra.Command {

cmd.Flags().StringP(FlagProposerAddress, "p", "", "--proposer=<proposer-address>")
cmd.Flags().String(FlagTxHash, "", "--tx-hash=<transaction-hash>")
cmd.Flags().Uint64(FlagBlockNumber, 0, "--block-number=<block-number>")
cmd.Flags().String(FlagLogIndex, "", "--log-index=<log-index>")
cmd.Flags().String(FlagAmount, "0", "--amount=<amount>")

if err := cmd.MarkFlagRequired(FlagBlockNumber); err != nil {
logger.Error("SendValidatorJoinTx | MarkFlagRequired | FlagBlockNumber", "Error", err)
}
cmd.MarkFlagRequired(FlagProposerAddress)
cmd.MarkFlagRequired(FlagTxHash)
cmd.MarkFlagRequired(FlagLogIndex)
cmd.MarkFlagRequired(FlagAmount)

return cmd
}
32 changes: 21 additions & 11 deletions slashing/client/rest/tx.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package rest

import (
"math/big"
"net/http"

"github.com/cosmos/cosmos-sdk/client/context"
Expand Down Expand Up @@ -35,23 +36,24 @@ func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router) {
type UnjailReq struct {
BaseReq rest.BaseReq `json:"base_req"`

ID uint64 `json:"ID"`
TxHash string `json:"tx_hash"`
LogIndex uint64 `json:"log_index"`
ID uint64 `json:"ID"`
TxHash string `json:"tx_hash"`
LogIndex uint64 `json:"log_index"`
BlockNumber uint64 `json:"block_number" yaml:"block_number"`
}

type TickReq struct {
BaseReq rest.BaseReq `json:"base_req"`

Proposer string `json:"proposer"`
SlashingInfoHash string `json:"slashing_info_hash"`
BaseReq rest.BaseReq `json:"base_req"`
Proposer string `json:"proposer"`
SlashingInfoHash string `json:"slashing_info_hash"`
}

type TickAckReq struct {
BaseReq rest.BaseReq `json:"base_req"`

TxHash string `json:"tx_hash"`
LogIndex uint64 `json:"log_index"`
BaseReq rest.BaseReq `json:"base_req"`
Amount string `json:"amount"`
TxHash string `json:"tx_hash"`
LogIndex uint64 `json:"log_index"`
BlockNumber uint64 `json:"block_number" yaml:"block_number"`
}

func newUnjailRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
Expand All @@ -73,6 +75,7 @@ func newUnjailRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
req.ID,
hmTypes.HexToHeimdallHash(req.TxHash),
req.LogIndex,
req.BlockNumber,
)
err := msg.ValidateBasic()
if err != nil {
Expand Down Expand Up @@ -111,10 +114,17 @@ func newTickAckHandler(cliCtx context.CLIContext) http.HandlerFunc {
return
}

amount, ok := big.NewInt(0).SetString(req.Amount, 10)
if !ok {
rest.WriteErrorResponse(w, http.StatusBadRequest, "invalid amount")
}

msg := types.NewMsgTickAck(
hmTypes.HexToHeimdallAddress(req.BaseReq.From),
amount,
hmTypes.HexToHeimdallHash(req.TxHash),
req.LogIndex,
req.BlockNumber,
)

restClient.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg})
Expand Down
154 changes: 146 additions & 8 deletions slashing/handler.go
Original file line number Diff line number Diff line change
@@ -1,27 +1,165 @@
package slashing

import (
"bytes"
"fmt"
"math/big"

sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/maticnetwork/heimdall/common"
"github.com/maticnetwork/heimdall/helper"
"github.com/maticnetwork/heimdall/slashing/types"
hmTypes "github.com/maticnetwork/heimdall/types"

hmCommon "github.com/maticnetwork/heimdall/common"
)

// NewHandler creates an sdk.Handler for all the slashing type messages
func NewHandler(k Keeper, contractCaller helper.IContractCaller) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
ctx = ctx.WithEventManager(sdk.NewEventManager())
return sdk.ErrTxDecode("Invalid message in slashing module").Result()
// switch msg := msg.(type) {
/* case types.MsgUnjail:
return handleMsgUnjail(ctx, msg, k, contractCaller)

switch msg := msg.(type) {
case types.MsgTick:
return handlerMsgTick(ctx, msg, k, contractCaller)
case types.MsgTickAck:
return handleMsgTickAck(ctx, msg, k, contractCaller) */
// default:
// return sdk.ErrTxDecode("Invalid message in slashing module").Result()
// }
return handleMsgTickAck(ctx, msg, k, contractCaller)
case types.MsgUnjail:
return handleMsgUnjail(ctx, msg, k, contractCaller)
default:
return sdk.ErrTxDecode("Invalid message in slashing module").Result()
}
}
}


// handlerMsgTick - handles slashing of validators
// 0. check if slashLimit is exceeded or not.
// 1. Validate input slashing info hash data
// 2. If hash matches, copy slashBuffer into latestTickData
// 3. flushes slashBuffer, totalSlashedAmount
// 4. iterate and reduce the power of slashed validators.
// 5. Also update the jailStatus of Validator
// 6. emit event TickConfirmation
func handlerMsgTick(ctx sdk.Context, msg types.MsgTick, k Keeper, contractCaller helper.IContractCaller) sdk.Result {

k.Logger(ctx).Debug("✅ Validating tick msg",
"SlashInfoHash", msg.SlashingInfoHash,
)

// check if slash limit is exceeded or not
if !k.IsSlashedLimitExceeded(ctx) {
k.Logger(ctx).Error("TotalSlashedAmount is less than SlashLimit")
return hmCommon.ErrInvalidMsg(k.Codespace(), fmt.Sprintf("TotalSlashedAmount %v is less than SlashLimit", k.GetTotalSlashedAmount(ctx))).Result()
}

valSlashingInfos, err := k.GetBufferValSlashingInfos(ctx)
if err != nil {
k.Logger(ctx).Error("Error fetching slash Info list from buffer", "error", err)
return hmCommon.ErrSlashInfoDetails(k.Codespace()).Result()
}

slashingInfoHash, err := types.GetSlashingInfoHash(valSlashingInfos)
if err != nil {
k.Logger(ctx).Info("Error generating slashing info hash", "error", err)
return hmCommon.ErrSlashInfoDetails(k.Codespace()).Result()
}

// compare slashingInfoHash with msg hash
k.Logger(ctx).Info("SlashInfo hash generated", "SlashInfoHash", hmTypes.BytesToHeimdallHash(slashingInfoHash).String())

if !bytes.Equal(slashingInfoHash, msg.SlashingInfoHash.Bytes()) {
k.Logger(ctx).Error("SlashInfoHash of current buffer state", "bufferSlashInfoHash", hmTypes.BytesToHeimdallHash(slashingInfoHash).String(),
"doesn't match with SlashInfoHash of msg", "msgSlashInfoHash", msg.SlashingInfoHash)
return hmCommon.ErrSlashInfoDetails(k.Codespace()).Result()
}

k.Logger(ctx).Debug("SlashInfoHash matches")

// ensure latestTickData is empty
tickSlashingInfos, err := k.GetTickValSlashingInfos(ctx)
if err != nil {
k.Logger(ctx).Error("Error fetching slash Info list from tick", "error", err)
return common.ErrSlashInfoDetails(k.Codespace()).Result()
}

if tickSlashingInfos != nil && len(tickSlashingInfos) > 0 {
k.Logger(ctx).Error("Waiting for tick data to be pushed to contract", "tickSlashingInfo", tickSlashingInfos)
return common.ErrSlashInfoDetails(k.Codespace()).Result()
}

return sdk.Result{
Events: ctx.EventManager().Events(),
}
}

// Validators must submit a transaction to unjail itself after
// having been jailed (and thus unbonded) for downtime
func handleMsgUnjail(ctx sdk.Context, msg types.MsgUnjail, k Keeper, contractCaller helper.IContractCaller) sdk.Result {

k.Logger(ctx).Debug("✅ Validating unjail msg",
"validatorId", msg.ID,
"txHash", hmTypes.BytesToHeimdallHash(msg.TxHash.Bytes()),
"logIndex", uint64(msg.LogIndex),
"blockNumber", msg.BlockNumber,
)

// sequence id
blockNumber := new(big.Int).SetUint64(msg.BlockNumber)
sequence := new(big.Int).Mul(blockNumber, big.NewInt(hmTypes.DefaultLogIndexUnit))
sequence.Add(sequence, new(big.Int).SetUint64(msg.LogIndex))

// check if incoming tx is older
if k.HasSlashingSequence(ctx, sequence.String()) {
k.Logger(ctx).Error("Older invalid tx found")
return hmCommon.ErrOldTx(k.Codespace()).Result()
}

// pull validator from store
validator, ok := k.sk.GetValidatorFromValID(ctx, msg.ID)
if !ok {
k.Logger(ctx).Error("Fetching of validator from store failed", "validatorId", msg.ID)
return hmCommon.ErrNoValidator(k.Codespace()).Result()
}


if !validator.Jailed {
k.Logger(ctx).Error("Fetching of validator from store failed", "validatorId", msg.ID)
return hmCommon.ErrNoValidator(k.Codespace()).Result()
}
return sdk.Result{
Events: ctx.EventManager().Events(),
}
}


/*
handleMsgTickAck - handle msg tick ack event
1. validate the tx hash in the event
2. flush the last tick slashing info
*/
func handleMsgTickAck(ctx sdk.Context, msg types.MsgTickAck, k Keeper, contractCaller helper.IContractCaller) sdk.Result {

k.Logger(ctx).Debug("✅ Validating TickAck msg",
"SlashedAmount", msg.SlashedAmount,
"txHash", hmTypes.BytesToHeimdallHash(msg.TxHash.Bytes()),
"logIndex", uint64(msg.LogIndex),
"blockNumber", msg.BlockNumber,
)

// sequence id
blockNumber := new(big.Int).SetUint64(msg.BlockNumber)
sequence := new(big.Int).Mul(blockNumber, big.NewInt(hmTypes.DefaultLogIndexUnit))
sequence.Add(sequence, new(big.Int).SetUint64(msg.LogIndex))

// check if incoming tx is older
if k.HasSlashingSequence(ctx, sequence.String()) {
k.Logger(ctx).Error("Older invalid tx found")
return hmCommon.ErrOldTx(k.Codespace()).Result()
}

return sdk.Result{
Events: ctx.EventManager().Events(),
}
}
Loading

0 comments on commit 793c46c

Please sign in to comment.