Skip to content

Commit

Permalink
feat x/bridge: added InboundTransfer storing (#7775)
Browse files Browse the repository at this point in the history
* feat x/bridge: added InboundTransfer storing

* added finalization checks

* added inbound transfer diagram

* Generated protofile changes

* code review 1

* code review 2

---------

Co-authored-by: github-actions <[email protected]>
  • Loading branch information
keruch and github-actions authored Mar 20, 2024
1 parent 452f273 commit 8b1e746
Show file tree
Hide file tree
Showing 16 changed files with 977 additions and 139 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ require (
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/gordonklaus/ineffassign v0.0.0-20230610083614-0e73809eb601 // indirect
github.com/gorilla/handlers v1.5.1 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/gorilla/websocket v1.5.0
github.com/gostaticanalysis/analysisutil v0.7.1 // indirect
github.com/gostaticanalysis/comment v1.4.2 // indirect
github.com/gostaticanalysis/forcetypeassert v0.1.0 // indirect
Expand Down
28 changes: 28 additions & 0 deletions proto/osmosis/bridge/v1beta1/bridge.proto
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,32 @@ enum AssetStatus {
ASSET_STATUS_BLOCKED_INBOUND = 2;
ASSET_STATUS_BLOCKED_OUTBOUND = 3;
ASSET_STATUS_BLOCKED_BOTH = 4;
}

// InboundTransfer is a representation of the inbound transfer.
message InboundTransfer {
// ExternalId is a unique ID of the transfer coming from outside.
// Serves the purpose of uniquely identifying the transfer in another chain
// (e.g., this might be the BTC tx hash).
string external_id = 1 [ (gogoproto.moretags) = "yaml:\"external_id\"" ];
// DestAddr is a destination Osmosis address
string dest_addr = 2 [ (gogoproto.moretags) = "yaml:\"dest_addr\"" ];
// AssetID is the ID of the asset being transferred
AssetID asset_id = 3 [
(gogoproto.moretags) = "yaml:\"asset_id\"",
(gogoproto.nullable) = false
];
// Amount of coins to transfer
string amount = 4 [
(gogoproto.moretags) = "yaml:\"amount\"",
(gogoproto.customtype) = "cosmossdk.io/math.Int",
(gogoproto.nullable) = false
];
// Voters is a list of validators signed this transfer
repeated string voters = 5 [ (gogoproto.moretags) = "yaml:\"voters\"" ];
// Finalized indicates whether the transfer needs more votes or has
// already accumulated a sufficient number. The finalised flag is set
// to true as soon as length(voters) is greater than or equal to
// the module's param votes_needed.
bool finalized = 6 [ (gogoproto.moretags) = "yaml:\"finalized\"" ];
}
12 changes: 8 additions & 4 deletions proto/osmosis/bridge/v1beta1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,21 @@ service Msg {
message MsgInboundTransfer {
option (amino.name) = "osmosis/bridge/inbound-transfer";

// ExternalId is a unique ID of the transfer coming from outside.
// Serves the purpose of uniquely identifying the transfer in another chain
// (e.g., this might be the BTC tx hash)
string external_id = 1 [ (gogoproto.moretags) = "yaml:\"external_id\"" ];
// Sender is a sender's address
string sender = 1 [ (gogoproto.moretags) = "yaml:\"sender\"" ];
string sender = 2 [ (gogoproto.moretags) = "yaml:\"sender\"" ];
// DestAddr is a destination Osmosis address
string dest_addr = 2 [ (gogoproto.moretags) = "yaml:\"dest_addr\"" ];
string dest_addr = 3 [ (gogoproto.moretags) = "yaml:\"dest_addr\"" ];
// AssetID is the ID of the asset being transferred
AssetID asset_id = 3 [
AssetID asset_id = 4 [
(gogoproto.moretags) = "yaml:\"asset_id\"",
(gogoproto.nullable) = false
];
// Amount of coins to transfer
string amount = 4 [
string amount = 5 [
(gogoproto.moretags) = "yaml:\"amount\"",
(gogoproto.customtype) = "cosmossdk.io/math.Int",
(gogoproto.nullable) = false
Expand Down
Binary file added x/bridge/images/inbound_transfer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
98 changes: 98 additions & 0 deletions x/bridge/images/inbound_transfer.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
@startuml

actor "Client" as client
node "BTC vault" as vault

client --> vault : send BTC

folder "Valset" as valset1 {
cloud "Validator 1" as val1 #lightgreen
cloud "Validator 2" as val2
cloud "Validator 3" as val3 #lightgreen
cloud "Validator 4" as val4
cloud "Validator 5" as val5 #lightgreen
}

note bottom of valset1
Green validators are
running x/bridge observers.

Let's say that we need **three**
votes to process the transfer.
end note

folder "Valset" as valset2 {
cloud "Validator 1" as val1_2 #lightgreen
cloud "Validator 2" as val2_2
cloud "Validator 3" as val3_2 #lightgreen
cloud "Validator 4" as val4_2
cloud "Validator 5" as val5_2 #lightgreen
}

vault <-- val1 : observe
vault <-- val3 : observe
vault <-- val5 : observe

node "Chain proposer" as proposer

val1 --> proposer : MsgInboundTransfer
val3 --> proposer : MsgInboundTransfer
val5 --> proposer : MsgInboundTransfer

json Block {
"1":"MsgInboundTransfer",
"2":"MsgInboundTransfer",
"3":"MsgInboundTransfer"
}

proposer --> Block : forms a block to process

Block <-- val1_2 : process
Block <-- val2_2 : process
Block <-- val3_2 : process
Block <-- val4_2 : process
Block <-- val5_2 : process

action "Process each\nMsgInboundTransfer" as val1_act_1
action "Process each\nMsgInboundTransfer" as val2_act_1
action "Process each\nMsgInboundTransfer" as val3_act_1
action "Process each\nMsgInboundTransfer" as val4_act_1
action "Process each\nMsgInboundTransfer" as val5_act_1

val1_2 --> val1_act_1
val2_2 --> val2_act_1
val3_2 --> val3_act_1
val4_2 --> val4_act_1
val5_2 --> val5_act_1

action "Accumulate votes x3" as val1_act_2
action "Is not a part\nof the signers set" as val2_act_2 #red
action "Accumulate votes x3" as val3_act_2
action "Is not a part\nof the signers set" as val4_act_2 #red
action "Accumulate votes x3" as val5_act_2

val1_act_1 --> val1_act_2
val2_act_1 --> val2_act_2
val3_act_1 --> val3_act_2
val4_act_1 --> val4_act_2
val5_act_1 --> val5_act_2

action "Call tokenfactory mint" as val1_act_3
action "Call tokenfactory mint" as val3_act_3
action "Call tokenfactory mint" as val5_act_3

val1_act_2 --> val1_act_3
val3_act_2 --> val3_act_3
val5_act_2 --> val5_act_3

node "Submit the block" as consensus

val1_act_3 --> consensus
val3_act_3 --> consensus
val5_act_3 --> consensus

note left of consensus
The transfer is done!
end note

@enduml
9 changes: 9 additions & 0 deletions x/bridge/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,18 @@ import (

"github.com/cometbft/cometbft/libs/log"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/codec"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"

"github.com/osmosis-labs/osmosis/v23/x/bridge/types"
)

type Keeper struct {
storeKey storetypes.StoreKey
cdc codec.BinaryCodec

// paramSpace stores module's params
paramSpace paramtypes.Subspace
// router is used to access tokenfactory methods
Expand All @@ -25,6 +30,8 @@ type Keeper struct {

// NewKeeper returns a new instance of the x/bridge keeper.
func NewKeeper(
storeKey storetypes.StoreKey,
cdc codec.BinaryCodec,
paramSpace paramtypes.Subspace,
router *baseapp.MsgServiceRouter,
accountKeeper types.AccountKeeper,
Expand All @@ -40,6 +47,8 @@ func NewKeeper(
}

return Keeper{
storeKey: storeKey,
cdc: cdc,
paramSpace: paramSpace,
router: router,
accountKeeper: accountKeeper,
Expand Down
24 changes: 22 additions & 2 deletions x/bridge/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,18 @@ func (m msgServer) InboundTransfer(
goCtx context.Context,
msg *types.MsgInboundTransfer,
) (*types.MsgInboundTransferResponse, error) {
err := msg.ValidateBasic()
if err != nil {
return nil, err
}

ctx := sdk.UnwrapSDKContext(goCtx)

if !m.k.validateSenderIsSigner(ctx, msg.Sender) {
return nil, errorsmod.Wrapf(sdkerrors.ErrorInvalidSigner, "Sender is not part of the signer set")
}

err := m.k.InboundTransfer(ctx, msg.DestAddr, msg.AssetId, msg.Amount)
err = m.k.InboundTransfer(ctx, msg.ExternalId, msg.Sender, msg.DestAddr, msg.AssetId, msg.Amount)
if err != nil {
return nil, err
}
Expand All @@ -54,11 +59,16 @@ func (m msgServer) OutboundTransfer(
goCtx context.Context,
msg *types.MsgOutboundTransfer,
) (*types.MsgOutboundTransferResponse, error) {
err := msg.ValidateBasic()
if err != nil {
return nil, err
}

ctx := sdk.UnwrapSDKContext(goCtx)

// Don't need to check the signature here since every user could be the sender

err := m.k.OutboundTransfer(ctx, msg.Sender, msg.AssetId, msg.Amount)
err = m.k.OutboundTransfer(ctx, msg.Sender, msg.AssetId, msg.Amount)
if err != nil {
return nil, err
}
Expand All @@ -81,6 +91,11 @@ func (m msgServer) UpdateParams(
goCtx context.Context,
msg *types.MsgUpdateParams,
) (*types.MsgUpdateParamsResponse, error) {
err := msg.ValidateBasic()
if err != nil {
return nil, err
}

ctx := sdk.UnwrapSDKContext(goCtx)

if msg.Sender != m.k.govModuleAddr {
Expand Down Expand Up @@ -113,6 +128,11 @@ func (m msgServer) ChangeAssetStatus(
goCtx context.Context,
msg *types.MsgChangeAssetStatus,
) (*types.MsgChangeAssetStatusResponse, error) {
err := msg.ValidateBasic()
if err != nil {
return nil, err
}

ctx := sdk.UnwrapSDKContext(goCtx)

result, err := m.k.ChangeAssetStatus(ctx, msg.AssetId, msg.NewStatus)
Expand Down
Loading

0 comments on commit 8b1e746

Please sign in to comment.