Skip to content

Commit

Permalink
fixup! feat(x/swingset): auto-provision smart wallet
Browse files Browse the repository at this point in the history
  • Loading branch information
mhofman committed Dec 1, 2023
1 parent c20d013 commit cf6f4d2
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 28 deletions.
4 changes: 4 additions & 0 deletions golang/cosmos/ante/inbound_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,3 +221,7 @@ func (msk mockSwingsetKeeper) ChargeBeans(ctx sdk.Context, addr sdk.AccAddress,
func (msk mockSwingsetKeeper) GetSmartWalletState(ctx sdk.Context, addr sdk.AccAddress) (swingtypes.SmartWalletState, error) {
return swingtypes.SmartWalletStateUnspecified, fmt.Errorf("not implemented")
}

func (msk mockSwingsetKeeper) ChargeForSmartWallet(ctx sdk.Context, addr sdk.AccAddress) error {
return fmt.Errorf("not implemented")
}
19 changes: 19 additions & 0 deletions golang/cosmos/x/swingset/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,25 @@ func (k Keeper) ChargeBeans(ctx sdk.Context, addr sdk.AccAddress, beans sdk.Uint
return nil
}

// ChargeForSmartWallet charges the fee for provisioning a smart wallet.
func (k Keeper) ChargeForSmartWallet(ctx sdk.Context, addr sdk.AccAddress) error {
beansPerUnit := k.GetBeansPerUnit(ctx)
beans := beansPerUnit[types.BeansPerSmartWalletProvision]
err := k.ChargeBeans(ctx, addr, beans)
if err != nil {
return err
}

// TODO: mark that a smart wallet provision is pending. However in that case,
// auto-provisioning should still be performed (but without fees being charged),
// until the controller actually provisions the smart wallet (the operation may
// transiently fail, requiring retries until success).
// However the provisioning code is not currently idempotent, and has side
// effects when the smart wallet is already provisioned.

return nil
}

// makeFeeMenu returns a map from power flag to its fee. In the case of duplicates, the
// first one wins.
func makeFeeMenu(powerFlagFees []types.PowerFlagFee) map[string]sdk.Coins {
Expand Down
7 changes: 3 additions & 4 deletions golang/cosmos/x/swingset/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,14 +146,13 @@ type provisionAction struct {
// non-provisioned smart wallets allowed by the admission AnteHandler should
// auto-provision the smart wallet.
func (keeper msgServer) provisionIfNeeded(ctx sdk.Context, owner sdk.AccAddress) error {
// TODO: If/when we implement marking of pending smart wallet provisions,
// here we'll need to generate a provision action until the smart wallet has
// We need to generate a provision action until the smart wallet has
// been fully provisioned by the controller. This is because a provision is
// not guaranteed to succeed (e.g. lack of provision pool funds)
provisioned, err := keeper.HasSmartWallet(ctx, owner)
walletState, err := keeper.GetSmartWalletState(ctx, owner)
if err != nil {
return err
} else if provisioned {
} else if walletState == types.SmartWalletStateProvisioned {
return nil
}

Expand Down
1 change: 1 addition & 0 deletions golang/cosmos/x/swingset/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ type SwingSetKeeper interface {
ChargeBeans(ctx sdk.Context, addr sdk.AccAddress, beans sdk.Uint) error
IsHighPriorityAddress(ctx sdk.Context, addr sdk.AccAddress) (bool, error)
GetSmartWalletState(ctx sdk.Context, addr sdk.AccAddress) (SmartWalletState, error)
ChargeForSmartWallet(ctx sdk.Context, addr sdk.AccAddress) error
}
39 changes: 15 additions & 24 deletions golang/cosmos/x/swingset/types/msgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,42 +49,33 @@ func chargeAdmission(ctx sdk.Context, keeper SwingSetKeeper, addr sdk.AccAddress
}

// checkSmartWalletProvisioned verifies if a smart wallet message can be
// delivered for the owner's address. A messaged is allowed if a smart wallet
// delivered for the owner's address. A message is allowed if a smart wallet
// is already provisioned for the address, or if the provisioning fee is
// charged successfully.
// All messages for non-provisioned smart wallets allowed here will result in
// an auto-provision action generated by the msg server.
func checkSmartWalletProvisioned(ctx sdk.Context, keeper SwingSetKeeper, addr sdk.AccAddress) error {
// This checks if a smart wallet has already been provisioned by the controller
// However a provision (either explicit or automatic) may be pending execution
// and this check will not catch that case, resulting in the owner being charged
// for provisioning again.
// Furthermore, while multiple swingset messages are not currently allowed in
// the same transaction, if that is ever relaxed, we would end up generating
// multiple provision actions, and the provisioning code is not fully
// idempotent. However this is only a degenerate case of the multiple txs case,
// and as such is charged again similarly.
// TODO: mark that a smart wallet provision is pending. However in that case,
// auto-provisioning should still be performed (but without fees being charged),
// until the controller actually provisions the smart wallet (the operation may
// transiently fail, requiring retries until success)
walletState, err := keeper.GetSmartWalletState(ctx, addr)
if err != nil {
return err
}

if !isProvisioned {
beansPerUnit := keeper.GetBeansPerUnit(ctx)
beans := beansPerUnit[BeansPerSmartWalletProvision]

switch walletState {
case SmartWalletStateProvisioned:
// The address already has a smart wallet
return nil
case SmartWalletStatePending:
// A provision (either explicit or automatic) may be pending execution in
// the controller, or if we ever allow multiple swingset messages per
// transaction, a previous message may have provisioned the wallet.
return nil
default:
// Charge for the smart wallet.
// This is a separate charge from the smart wallet action which triggered the check
err = keeper.ChargeBeans(ctx, addr, beans)
if err != nil {
return err
}
// TODO: Currently this call does not mark the smart wallet provisioning as
// pending, resulting in multiple provisioning charges for the owner.
return keeper.ChargeForSmartWallet(ctx, addr)
}

return nil
}

func NewMsgDeliverInbound(msgs *Messages, submitter sdk.AccAddress) *MsgDeliverInbound {
Expand Down

0 comments on commit cf6f4d2

Please sign in to comment.