Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: slashing unsign bridge call #321

Merged
merged 1 commit into from
Apr 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 17 additions & 17 deletions proto/fx/crosschain/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -78,17 +78,17 @@ service Query {
rpc GetPendingSendToExternal(QueryPendingSendToExternalRequest) returns (QueryPendingSendToExternalResponse) {
option (google.api.http).get = "/fx/crosschain/v1/pending_send_to_external";
}
rpc RefundRecordByNonce(QueryRefundRecordByNonceRequest) returns (QueryRefundRecordByNonceResponse) {
option (google.api.http).get = "/fx/crosschain/v1/refund_record_by_nonce";
}
rpc RefundRecordByReceiver(QueryRefundRecordByReceiverRequest) returns (QueryRefundRecordByReceiverResponse) {
option (google.api.http).get = "/fx/crosschain/v1/refund_record_by_receiver";
}
rpc BridgeCallConfirmByNonce(QueryBridgeCallConfirmByNonceRequest) returns (QueryBridgeCallConfirmByNonceResponse) {
option (google.api.http).get = "/fx/crosschain/v1/bridge_call_confirm_by_nonce";
}
rpc LastPendingRefundRecordByAddr(QueryLastPendingRefundRecordByAddrRequest) returns (QueryLastPendingRefundRecordByAddrResponse) {
option (google.api.http).get = "/fx/crosschain/v1/last_pending_refund_record_by_addr";
rpc BridgeCallByNonce(QueryBridgeCallByNonceRequest) returns (QueryBridgeCallByNonceResponse) {
option (google.api.http).get = "/fx/crosschain/v1/bridge_call_by_nonce";
}
rpc BridgeCallByReceiver(QueryBridgeCallByReceiverRequest) returns (QueryBridgeCallByReceiverResponse) {
option (google.api.http).get = "/fx/crosschain/v1/bridge_call_by_receiver";
}
rpc LastPendingBridgeCallByAddr(QueryLastPendingBridgeCallByAddrRequest) returns (QueryLastPendingBridgeCallByAddrResponse) {
option (google.api.http).get = "/fx/crosschain/v1/last_pending_bridge_call_by_addr";
}

// Validators queries all oracle that match the given status.
Expand Down Expand Up @@ -314,20 +314,20 @@ message QueryBridgeChainListResponse {
repeated string chain_names = 1;
}

message QueryRefundRecordByNonceRequest {
message QueryBridgeCallByNonceRequest {
string chain_name = 1;
uint64 event_nonce = 2;
}
message QueryRefundRecordByNonceResponse {
RefundRecord record = 1;
message QueryBridgeCallByNonceResponse {
OutgoingBridgeCall bridge_call = 1;
}

message QueryRefundRecordByReceiverRequest {
message QueryBridgeCallByReceiverRequest {
string chain_name = 1;
string receiver_address = 2;
}
message QueryRefundRecordByReceiverResponse {
repeated RefundRecord records = 1;
message QueryBridgeCallByReceiverResponse {
repeated OutgoingBridgeCall bridge_calls = 1;
}

message QueryBridgeCallConfirmByNonceRequest {
Expand All @@ -340,10 +340,10 @@ message QueryBridgeCallConfirmByNonceResponse {
bool enough_power = 2;
}

message QueryLastPendingRefundRecordByAddrRequest {
message QueryLastPendingBridgeCallByAddrRequest {
string chain_name = 1;
string external_address = 2;
}
message QueryLastPendingRefundRecordByAddrResponse {
repeated RefundRecord records = 1;
message QueryLastPendingBridgeCallByAddrResponse {
repeated OutgoingBridgeCall bridge_call = 1;
}
9 changes: 0 additions & 9 deletions proto/fx/crosschain/v1/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -122,15 +122,6 @@ message OutgoingTransferTx {
ERC20Token fee = 5 [(gogoproto.nullable) = false];
}

message RefundRecord {
uint64 event_nonce = 1;
string receiver = 2;
uint64 timeout = 3;
repeated ERC20Token tokens = 4 [(gogoproto.nullable) = false];
uint64 oracle_set_nonce = 5;
uint64 block = 6;
}

message SnapshotOracle {
uint64 oracle_set_nonce = 1;
repeated BridgeValidator members = 2 [(gogoproto.nullable) = false];
Expand Down
32 changes: 16 additions & 16 deletions x/crosschain/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,11 @@ func GetQuerySubCmds(chainName string) []*cobra.Command {
// help cmd.
CmdCovertBridgeToken(chainName),

// refund token
CmdRefundRecord(chainName),
CmdRefundRecordByAddr(chainName),
// bridge call
CmdBridgeCall(chainName),
CmdBridgeCallByAddr(chainName),
CmdBridgeCallConfirm(chainName),
CmdLastPendingRefundRecord(chainName),
CmdLastPendingBridgeCall(chainName),
}

for _, command := range cmds {
Expand Down Expand Up @@ -828,10 +828,10 @@ func CmdGetBridgeCoinByDenom(chainName string) *cobra.Command {
return cmd
}

func CmdRefundRecord(chainName string) *cobra.Command {
func CmdBridgeCall(chainName string) *cobra.Command {
cmd := &cobra.Command{
Use: "refund-record [nonce]",
Short: "Query refund record by event nonce",
Use: "bridge-call [nonce]",
Short: "Query bridge call by event nonce",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx := client.GetClientContextFromCmd(cmd)
Expand All @@ -842,7 +842,7 @@ func CmdRefundRecord(chainName string) *cobra.Command {
return err
}

res, err := queryClient.RefundRecordByNonce(cmd.Context(), &types.QueryRefundRecordByNonceRequest{
res, err := queryClient.BridgeCallByNonce(cmd.Context(), &types.QueryBridgeCallByNonceRequest{
ChainName: chainName,
EventNonce: nonce,
})
Expand All @@ -855,10 +855,10 @@ func CmdRefundRecord(chainName string) *cobra.Command {
return cmd
}

func CmdRefundRecordByAddr(chainName string) *cobra.Command {
func CmdBridgeCallByAddr(chainName string) *cobra.Command {
cmd := &cobra.Command{
Use: "refund-record-by-receiver [address]",
Short: "Query refund records by receiver",
Use: "bridge-call-by-receiver [address]",
Short: "Query bridge call by receiver",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx := client.GetClientContextFromCmd(cmd)
Expand All @@ -868,7 +868,7 @@ func CmdRefundRecordByAddr(chainName string) *cobra.Command {
if err != nil {
return err
}
res, err := queryClient.RefundRecordByReceiver(cmd.Context(), &types.QueryRefundRecordByReceiverRequest{
res, err := queryClient.BridgeCallByReceiver(cmd.Context(), &types.QueryBridgeCallByReceiverRequest{
ChainName: chainName,
ReceiverAddress: receiver,
})
Expand Down Expand Up @@ -908,10 +908,10 @@ func CmdBridgeCallConfirm(chainName string) *cobra.Command {
return cmd
}

func CmdLastPendingRefundRecord(chainName string) *cobra.Command {
func CmdLastPendingBridgeCall(chainName string) *cobra.Command {
cmd := &cobra.Command{
Use: "last-pending-refund-record [external-address]",
Short: "Query last pending refund record for bridge address",
Use: "last-pending-bridge-call [external-address]",
Short: "Query last pending bridge call for bridge address",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx := client.GetClientContextFromCmd(cmd)
Expand All @@ -921,7 +921,7 @@ func CmdLastPendingRefundRecord(chainName string) *cobra.Command {
if err != nil {
return err
}
res, err := queryClient.LastPendingRefundRecordByAddr(cmd.Context(), &types.QueryLastPendingRefundRecordByAddrRequest{
res, err := queryClient.LastPendingBridgeCallByAddr(cmd.Context(), &types.QueryLastPendingBridgeCallByAddrRequest{
ChainName: chainName,
ExternalAddress: externalAddress,
})
Expand Down
63 changes: 11 additions & 52 deletions x/crosschain/keeper/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ func (k Keeper) slashing(ctx sdk.Context, signedWindow uint64) {
oracles := k.GetAllOracles(ctx, true)
oracleSetHasSlash := k.oracleSetSlashing(ctx, oracles, signedWindow)
batchHasSlash := k.batchSlashing(ctx, oracles, signedWindow)
refundHasSlash := k.refundSlashing(ctx, signedWindow)
if oracleSetHasSlash || batchHasSlash || refundHasSlash {
bridgeCallHasSlash := k.bridgeCallSlashing(ctx, signedWindow)
if oracleSetHasSlash || batchHasSlash || bridgeCallHasSlash {
k.CommonSetOracleTotalPower(ctx)
}
}
Expand Down Expand Up @@ -122,14 +122,14 @@ func (k Keeper) batchSlashing(ctx sdk.Context, oracles types.Oracles, signedWind
return hasSlash
}

func (k Keeper) refundSlashing(ctx sdk.Context, signedWindow uint64) (hasSlash bool) {
func (k Keeper) bridgeCallSlashing(ctx sdk.Context, signedWindow uint64) (hasSlash bool) {
maxHeight := uint64(ctx.BlockHeight()) - signedWindow
unSlashRefunds := k.GetUnSlashedRefundRecords(ctx, maxHeight)
unSlashOutgoingBridgeCalls := k.GetUnSlashedBridgeCalls(ctx, maxHeight)

snapshotOracleMap := make(map[uint64]*types.SnapshotOracle)
for _, record := range unSlashRefunds {
for _, record := range unSlashOutgoingBridgeCalls {
confirmOracleMap := make(map[string]bool)
k.IterBridgeCallConfirmByNonce(ctx, record.EventNonce, func(confirm *types.MsgBridgeCallConfirm) bool {
k.IterBridgeCallConfirmByNonce(ctx, record.Nonce, func(confirm *types.MsgBridgeCallConfirm) bool {
confirmOracleMap[confirm.ExternalAddress] = true
return false
})
Expand All @@ -138,7 +138,7 @@ func (k Keeper) refundSlashing(ctx sdk.Context, signedWindow uint64) (hasSlash b
if !found {
snapshotOracle, found = k.GetSnapshotOracle(ctx, record.OracleSetNonce)
if !found {
k.Logger(ctx).Error("refund slashing", "oracle set not found", record.OracleSetNonce)
k.Logger(ctx).Error("outgoing bridge call slashing", "oracle set not found", record.OracleSetNonce)
continue
}
snapshotOracleMap[record.OracleSetNonce] = snapshotOracle
Expand All @@ -148,16 +148,16 @@ func (k Keeper) refundSlashing(ctx sdk.Context, signedWindow uint64) (hasSlash b
if _, ok := confirmOracleMap[members.ExternalAddress]; !ok {
oracle, found := k.GetOracleByExternalAddress(ctx, members.ExternalAddress)
if !found {
k.Logger(ctx).Error("refund slashing", "oracle not found", members.ExternalAddress)
k.Logger(ctx).Error("outgoing bridge call slashing", "oracle not found", members.ExternalAddress)
continue
}
k.Logger(ctx).Info("slash oracle by refund", "externalAddress", members.ExternalAddress,
"oracleAddress", oracle.String(), "recordNonce", record.EventNonce, "blockHeight", ctx.BlockHeight())
k.Logger(ctx).Info("slash oracle by outgoing bridge call", "externalAddress", members.ExternalAddress,
"oracleAddress", oracle.String(), "nonce", record.Nonce, "blockHeight", ctx.BlockHeight())
k.SlashOracle(ctx, oracle.String())
hasSlash = true
}
}
k.SetLastSlashedRefundNonce(ctx, record.EventNonce)
k.SetLastSlashedBridgeCallNonce(ctx, record.Nonce)
}
return hasSlash
}
Expand Down Expand Up @@ -186,47 +186,6 @@ func (k Keeper) cleanupTimedOutBatches(ctx sdk.Context) {
})
}

func (k Keeper) cleanupTimeOutRefund(ctx sdk.Context) {
externalBlockHeight := k.GetLastObservedBlockHeight(ctx).ExternalBlockHeight
k.IterRefundRecord(ctx, func(record *types.RefundRecord) bool {
if record.Timeout > externalBlockHeight {
return true
}
receiver, coins, err := k.refundTokenToReceiver(ctx, record)
if err != nil {
k.Logger(ctx).Error("clean up refund timeout", "event nonce", record.EventNonce, "error", err.Error())
return false
}
k.DeleteRefundRecord(ctx, record)
k.DeleteBridgeCallConfirm(ctx, record.EventNonce)
k.RemoveEventSnapshotOracle(ctx, record.OracleSetNonce, record.EventNonce)

ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(types.EventTypeRefundTimeout,
sdk.NewAttribute(sdk.AttributeKeySender, record.Receiver),
sdk.NewAttribute(types.AttributeKeyRefundAddress, receiver.String()),
sdk.NewAttribute(types.AttributeKeyEventNonce, fmt.Sprint(record.EventNonce)),
sdk.NewAttribute(sdk.AttributeKeyAmount, coins.String()),
),
})
return false
})
}

func (k Keeper) refundTokenToReceiver(ctx sdk.Context, record *types.RefundRecord) (sdk.AccAddress, sdk.Coins, error) {
receiverAddr := types.ExternalAddressToAccAddress(k.moduleName, record.Receiver)
cacheCtx, commit := ctx.CacheContext()
coins, err := k.bridgeCallTransferToSender(cacheCtx, receiverAddr.Bytes(), record.Tokens)
if err != nil {
return nil, nil, err
}
if err = k.bridgeCallTransferToReceiver(cacheCtx, receiverAddr, receiverAddr.Bytes(), coins); err != nil {
return nil, nil, err
}
commit()
return receiverAddr, coins, nil
}

func (k Keeper) cleanupTimeOutBridgeCall(ctx sdk.Context) {
externalBlockHeight := k.GetLastObservedBlockHeight(ctx).ExternalBlockHeight
k.IterateOutgoingBridgeCalls(ctx, func(data *types.OutgoingBridgeCall) bool {
Expand Down
Loading
Loading