|
| 1 | +--- |
| 2 | +title: Support the new StackBuilder primitive for Wiring Middlewares in the chain application |
| 3 | +sidebar_label: Support StackBuilder Wiring |
| 4 | +sidebar_position: 1 |
| 5 | +slug: /migrations/support-stackbuilder |
| 6 | +--- |
| 7 | + |
| 8 | +# Migration for Chains wishing to use StackBuilder |
| 9 | + |
| 10 | +The StackBuilder struct is a new primitive for wiring middleware in a simpler and less error-prone manner. It is not a breaking change thus the existing method of wiring middleware still works, though it is highly recommended to transition to the new wiring method. |
| 11 | + |
| 12 | +Refer to the [integration guide](../01-ibc/04-middleware/03-integration.md) to understand how to use this new middleware to improve middleware wiring in the chain application setup. |
| 13 | + |
| 14 | +# Migrations for Application Developers |
| 15 | + |
| 16 | +In order to be wired with the new StackBuilder primitive, applications and middlewares must implement new methods as part of their respective interfaces. |
| 17 | + |
| 18 | +IBC Applications must implement a new `SetICS4Wrapper` which will set the `ICS4Wrapper` through which the application will call `SendPacket` and `WriteAcknowledgement`. It is recommended that IBC applications are initialized first with the IBC ChannelKeeper directly, and then modified with a middleware ICS4Wrapper during the stack wiring. |
| 19 | + |
| 20 | +```go |
| 21 | +// SetICS4Wrapper sets the ICS4Wrapper. This function may be used after |
| 22 | +// the module's initialization to set the middleware which is above this |
| 23 | +// module in the IBC application stack. |
| 24 | +// The ICS4Wrapper **must** be used for sending packets and writing acknowledgements |
| 25 | +// to ensure that the middleware can intercept and process these calls. |
| 26 | +// Do not use the channel keeper directly to send packets or write acknowledgements |
| 27 | +// as this will bypass the middleware. |
| 28 | +SetICS4Wrapper(wrapper ICS4Wrapper) |
| 29 | +``` |
| 30 | + |
| 31 | +Many applications have a stateful keeper that executes the logic for sending packets and writing acknowledgements. In this case, the keeper in the application must be a **pointer** reference so that it can be modified in place after initialization. |
| 32 | + |
| 33 | +The initialization should be modified to no longer take in an addition `ics4Wrapper` as this gets modified later by `SetICS4Wrapper`. The constructor function must also return a **pointer** reference so that it may be modified in-place by the stack builder. |
| 34 | + |
| 35 | +Below is an example IBCModule that supports the stack builder wiring. |
| 36 | + |
| 37 | +E.g. |
| 38 | + |
| 39 | +```go |
| 40 | +type IBCModule struct { |
| 41 | + keeper *keeper.Keeper |
| 42 | +} |
| 43 | + |
| 44 | +// NewIBCModule creates a new IBCModule given the keeper |
| 45 | +func NewIBCModule(k *keeper.Keeper) *IBCModule { |
| 46 | + return &IBCModule{ |
| 47 | + keeper: k, |
| 48 | + } |
| 49 | +} |
| 50 | + |
| 51 | +// SetICS4Wrapper sets the ICS4Wrapper. This function may be used after |
| 52 | +// the module's initialization to set the middleware which is above this |
| 53 | +// module in the IBC application stack. |
| 54 | +func (im IBCModule) SetICS4Wrapper(wrapper porttypes.ICS4Wrapper) { |
| 55 | + if wrapper == nil { |
| 56 | + panic("ICS4Wrapper cannot be nil") |
| 57 | + } |
| 58 | + |
| 59 | + im.keeper.WithICS4Wrapper(wrapper) |
| 60 | +} |
| 61 | + |
| 62 | +/// Keeper file that has ICS4Wrapper internal to its own struct |
| 63 | + |
| 64 | +// Keeper defines the IBC fungible transfer keeper |
| 65 | +type Keeper struct { |
| 66 | + ... |
| 67 | + ics4Wrapper porttypes.ICS4Wrapper |
| 68 | + |
| 69 | + // Keeper is initialized with ICS4Wrapper |
| 70 | + // being equal to the top-level channelKeeper |
| 71 | + // this can be changed by calling WithICS4Wrapper |
| 72 | + // with a different middleware ICS4Wrapper |
| 73 | + channelKeeper types.ChannelKeeper |
| 74 | + ... |
| 75 | +} |
| 76 | + |
| 77 | +// WithICS4Wrapper sets the ICS4Wrapper. This function may be used after |
| 78 | +// the keepers creation to set the middleware which is above this module |
| 79 | +// in the IBC application stack. |
| 80 | +func (k *Keeper) WithICS4Wrapper(wrapper porttypes.ICS4Wrapper) { |
| 81 | + k.ics4Wrapper = wrapper |
| 82 | +} |
| 83 | +``` |
| 84 | + |
| 85 | +# Migration for Middleware Developers |
| 86 | + |
| 87 | +Since Middleware is itself implement the IBC application interface, it must also implement `SetICS4Wrapper` in the same way as IBC applications. |
| 88 | + |
| 89 | +Additionally, IBC Middleware has an underlying IBC application that it calls into as well. Previously this application would be set in the middleware upon construction. With the stack builder primitive, the application is only set during upon calling `stack.Build()`. Thus, middleware is additionally responsible for implementing the new method: `SetUnderlyingApplication`: |
| 90 | + |
| 91 | +```go |
| 92 | +// SetUnderlyingModule sets the underlying IBC module. This function may be used after |
| 93 | +// the middleware's initialization to set the ibc module which is below this middleware. |
| 94 | +SetUnderlyingApplication(IBCModule) |
| 95 | +``` |
| 96 | + |
| 97 | +The initialization should not include the ICS4Wrapper and application as this gets set later. The constructor function for Middlewares **must** be modified to return a **pointer** reference so that it can be modified in place by the stack builder. |
| 98 | + |
| 99 | +Below is an example middleware setup: |
| 100 | + |
| 101 | +```go |
| 102 | +// IBCMiddleware implements the ICS26 callbacks |
| 103 | +type IBCMiddleware struct { |
| 104 | + app porttypes.PacketUnmarshalerModule |
| 105 | + ics4Wrapper porttypes.ICS4Wrapper |
| 106 | + |
| 107 | + // this is a stateful middleware with its own internal keeper |
| 108 | + mwKeeper *keeper.MiddlewareKeeper |
| 109 | + |
| 110 | + // this is a middleware specific field |
| 111 | + mwField any |
| 112 | +} |
| 113 | + |
| 114 | +// NewIBCMiddleware creates a new IBCMiddleware given the keeper and underlying application. |
| 115 | +// NOTE: It **must** return a pointer reference so it can be |
| 116 | +// modified in place by the stack builder |
| 117 | +// NOTE: We do not pass in the underlying app and ICS4Wrapper here as this happens later |
| 118 | +func NewIBCMiddleware( |
| 119 | + mwKeeper *keeper.MiddlewareKeeper, mwField any, |
| 120 | +) *IBCMiddleware { |
| 121 | + return &IBCMiddleware{ |
| 122 | + mwKeeper: mwKeeper, |
| 123 | + mwField, mwField, |
| 124 | + } |
| 125 | +} |
| 126 | + |
| 127 | +// SetICS4Wrapper sets the ICS4Wrapper. This function may be used after the |
| 128 | +// middleware's creation to set the middleware which is above this module in |
| 129 | +// the IBC application stack. |
| 130 | +func (im *IBCMiddleware) SetICS4Wrapper(wrapper porttypes.ICS4Wrapper) { |
| 131 | + if wrapper == nil { |
| 132 | + panic("ICS4Wrapper cannot be nil") |
| 133 | + } |
| 134 | + im.mwKeeper.WithICS4Wrapper(wrapper) |
| 135 | +} |
| 136 | + |
| 137 | +// SetUnderlyingApplication sets the underlying IBC module. This function may be used after |
| 138 | +// the middleware's creation to set the ibc module which is below this middleware. |
| 139 | +func (im *IBCMiddleware) SetUnderlyingApplication(app porttypes.IBCModule) { |
| 140 | + if app == nil { |
| 141 | + panic(errors.New("underlying application cannot be nil")) |
| 142 | + } |
| 143 | + if im.app != nil { |
| 144 | + panic(errors.New("underlying application already set")) |
| 145 | + } |
| 146 | + im.app = app |
| 147 | +} |
| 148 | +``` |
0 commit comments