Skip to content

Commit f5926a3

Browse files
authored
Merge pull request #2974 from Agoric/mfig-2960-virtual-purses
feat: first cut at a virtual purse API
2 parents b3f0629 + 878537d commit f5926a3

34 files changed

+2137
-67
lines changed

golang/cosmos/app/app.go

+50-14
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ import (
9292
gaiaappparams "github.com/Agoric/agoric-sdk/golang/cosmos/app/params"
9393
"github.com/Agoric/agoric-sdk/golang/cosmos/x/dibc"
9494
"github.com/Agoric/agoric-sdk/golang/cosmos/x/swingset"
95+
"github.com/Agoric/agoric-sdk/golang/cosmos/x/vpurse"
9596

9697
// unnamed import of statik for swagger UI support
9798
_ "github.com/cosmos/cosmos-sdk/client/docs/statik"
@@ -123,6 +124,7 @@ var (
123124
ibc.AppModuleBasic{},
124125
swingset.AppModuleBasic{},
125126
dibc.AppModuleBasic{},
127+
vpurse.AppModuleBasic{},
126128
upgrade.AppModuleBasic{},
127129
evidence.AppModuleBasic{},
128130
transfer.AppModuleBasic{},
@@ -138,6 +140,7 @@ var (
138140
stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking},
139141
govtypes.ModuleName: {authtypes.Burner},
140142
ibctransfertypes.ModuleName: {authtypes.Minter, authtypes.Burner},
143+
vpurse.ModuleName: {authtypes.Minter, authtypes.Burner},
141144
}
142145
)
143146

@@ -155,7 +158,8 @@ type GaiaApp struct { // nolint: golint
155158
appCodec codec.Marshaler
156159
interfaceRegistry types.InterfaceRegistry
157160

158-
ibcPort int
161+
ibcPort int
162+
vpursePort int
159163

160164
invCheckPeriod uint
161165

@@ -184,6 +188,7 @@ type GaiaApp struct { // nolint: golint
184188

185189
SwingSetKeeper swingset.Keeper
186190
DibcKeeper dibc.Keeper
191+
VpurseKeeper vpurse.Keeper
187192

188193
// make scoped keepers public for test purposes
189194
ScopedIBCKeeper capabilitykeeper.ScopedKeeper
@@ -240,7 +245,9 @@ func NewAgoricApp(
240245
authtypes.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey,
241246
minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey,
242247
govtypes.StoreKey, paramstypes.StoreKey, ibchost.StoreKey, upgradetypes.StoreKey,
243-
evidencetypes.StoreKey, ibctransfertypes.StoreKey, swingset.StoreKey, dibc.StoreKey, capabilitytypes.StoreKey,
248+
evidencetypes.StoreKey, ibctransfertypes.StoreKey,
249+
swingset.StoreKey, dibc.StoreKey, vpurse.StoreKey,
250+
capabilitytypes.StoreKey,
244251
)
245252
tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey)
246253
memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey)
@@ -325,6 +332,7 @@ func NewAgoricApp(
325332

326333
// This function is tricky to get right, so we build it ourselves.
327334
callToController := func(ctx sdk.Context, str string) (string, error) {
335+
app.MustInitController(ctx)
328336
defer swingset.SetControllerContext(ctx)()
329337
return sendToController(true, str)
330338
}
@@ -355,6 +363,14 @@ func NewAgoricApp(
355363
ibcRouter.AddRoute(dibc.ModuleName, dibcModule)
356364
app.IBCKeeper.SetRouter(ibcRouter)
357365

366+
app.VpurseKeeper = vpurse.NewKeeper(
367+
appCodec, keys[vpurse.StoreKey],
368+
app.BankKeeper,
369+
callToController,
370+
)
371+
vpurseModule := vpurse.NewAppModule(app.VpurseKeeper)
372+
app.vpursePort = swingset.RegisterPortHandler("bank", vpurse.NewPortHandler(app.VpurseKeeper))
373+
358374
// create evidence keeper with router
359375
evidenceKeeper := evidencekeeper.NewKeeper(
360376
appCodec, keys[evidencetypes.StoreKey], &app.StakingKeeper, app.SlashingKeeper,
@@ -396,6 +412,7 @@ func NewAgoricApp(
396412
params.NewAppModule(app.ParamsKeeper),
397413
swingset.NewAppModule(app.SwingSetKeeper),
398414
dibcModule,
415+
vpurseModule,
399416
transferModule,
400417
)
401418

@@ -407,7 +424,7 @@ func NewAgoricApp(
407424
upgradetypes.ModuleName, minttypes.ModuleName, distrtypes.ModuleName, slashingtypes.ModuleName,
408425
evidencetypes.ModuleName, stakingtypes.ModuleName, ibchost.ModuleName, swingset.ModuleName,
409426
)
410-
app.mm.SetOrderEndBlockers(swingset.ModuleName, crisistypes.ModuleName, govtypes.ModuleName, stakingtypes.ModuleName)
427+
app.mm.SetOrderEndBlockers(vpurse.ModuleName, swingset.ModuleName, crisistypes.ModuleName, govtypes.ModuleName, stakingtypes.ModuleName)
411428

412429
// NOTE: The genutils module must occur after staking so that pools are
413430
// properly initialized with tokens from genesis accounts.
@@ -417,7 +434,9 @@ func NewAgoricApp(
417434
app.mm.SetOrderInitGenesis(
418435
capabilitytypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, distrtypes.ModuleName, stakingtypes.ModuleName,
419436
slashingtypes.ModuleName, govtypes.ModuleName, minttypes.ModuleName, crisistypes.ModuleName,
420-
ibchost.ModuleName, genutiltypes.ModuleName, evidencetypes.ModuleName, swingset.ModuleName, ibctransfertypes.ModuleName,
437+
ibchost.ModuleName, genutiltypes.ModuleName, evidencetypes.ModuleName,
438+
ibctransfertypes.ModuleName,
439+
vpurse.ModuleName, swingset.ModuleName,
421440
)
422441

423442
app.mm.RegisterInvariants(&app.CrisisKeeper)
@@ -484,10 +503,14 @@ func NewAgoricApp(
484503
}
485504

486505
type cosmosInitAction struct {
487-
Type string `json:"type"`
488-
IBCPort int `json:"ibcPort"`
489-
StoragePort int `json:"storagePort"`
490-
ChainID string `json:"chainID"`
506+
Type string `json:"type"`
507+
IBCPort int `json:"ibcPort"`
508+
StoragePort int `json:"storagePort"`
509+
VPursePort int `json:"vpursePort"`
510+
ChainID string `json:"chainID"`
511+
BootstrapAddress string `json:"bootstrapAddress"`
512+
BootstrapValue string `json:"bootstrapValue"`
513+
DonationValue string `json:"donationValue"`
491514
}
492515

493516
// MakeCodecs constructs the *std.Codec and *codec.LegacyAmino instances used by
@@ -507,12 +530,27 @@ func (app *GaiaApp) MustInitController(ctx sdk.Context) {
507530
}
508531
app.controllerInited = true
509532

533+
var bootstrapAddr sdk.AccAddress
534+
gs := app.VpurseKeeper.GetGenesis(ctx)
535+
if len(gs.BootstrapAddress) > 0 {
536+
ba, err := sdk.AccAddressFromBech32(gs.BootstrapAddress)
537+
if err != nil {
538+
fmt.Fprintln(os.Stderr, "Cannot get bootstrap addr", err)
539+
os.Exit(1)
540+
}
541+
bootstrapAddr = ba
542+
}
543+
510544
// Begin initializing the controller here.
511545
action := &cosmosInitAction{
512-
Type: "AG_COSMOS_INIT",
513-
IBCPort: app.ibcPort,
514-
StoragePort: swingset.GetPort("storage"),
515-
ChainID: ctx.ChainID(),
546+
Type: "AG_COSMOS_INIT",
547+
VPursePort: app.vpursePort,
548+
IBCPort: app.ibcPort,
549+
StoragePort: swingset.GetPort("storage"),
550+
ChainID: ctx.ChainID(),
551+
BootstrapAddress: bootstrapAddr.String(),
552+
BootstrapValue: gs.BootstrapValue.String(),
553+
DonationValue: gs.DonationValue.String(),
516554
}
517555
bz, err := json.Marshal(action)
518556
if err == nil {
@@ -526,7 +564,6 @@ func (app *GaiaApp) MustInitController(ctx sdk.Context) {
526564

527565
// BeginBlocker application updates every begin block
528566
func (app *GaiaApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
529-
app.MustInitController(ctx)
530567
return app.mm.BeginBlock(ctx, req)
531568
}
532569

@@ -556,7 +593,6 @@ func updateTransferPort(gs GenesisState, reservedPort, newPort string) error {
556593

557594
// InitChainer application update at chain initialization
558595
func (app *GaiaApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
559-
app.MustInitController(ctx)
560596
var genesisState GenesisState
561597
if err := tmjson.Unmarshal(req.AppStateBytes, &genesisState); err != nil {
562598
panic(err)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
syntax = "proto3";
2+
package agoric.vpurse;
3+
4+
import "gogoproto/gogo.proto";
5+
6+
option go_package = "github.com/Agoric/agoric-sdk/golang/cosmos/x/vpurse/types";
7+
8+
message GenesisState {
9+
option (gogoproto.equal) = false;
10+
11+
// These are passed as part of the AG_COSMOS_INIT message.
12+
// They are plumbed through to configure the bootstrap of the treasury:
13+
14+
// Cosmos-SDK layer bech32 address to receive bootstrap_value urun.
15+
string bootstrap_address = 1;
16+
17+
// Number of urun to have the treasury send to bootstrap_address.
18+
string bootstrap_value = 2 [
19+
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
20+
(gogoproto.nullable) = false
21+
];
22+
23+
// Number of urun to send from bootstrap_address to new ag-solo clients.
24+
string donation_value = 3 [
25+
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
26+
(gogoproto.nullable) = false
27+
];
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
syntax = "proto3";
2+
package agoric.vpurse;
3+
4+
option go_package = "github.com/Agoric/agoric-sdk/golang/cosmos/x/vpurse/types";
5+
6+
service Msg {
7+
}

golang/cosmos/x/vpurse/alias.go

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package vpurse
2+
3+
import (
4+
"github.com/Agoric/agoric-sdk/golang/cosmos/x/vpurse/keeper"
5+
"github.com/Agoric/agoric-sdk/golang/cosmos/x/vpurse/types"
6+
)
7+
8+
const (
9+
ModuleName = types.ModuleName
10+
RouterKey = types.RouterKey
11+
StoreKey = types.StoreKey
12+
)
13+
14+
var (
15+
NewKeeper = keeper.NewKeeper
16+
ModuleCdc = types.ModuleCdc
17+
RegisterCodec = types.RegisterCodec
18+
)
19+
20+
type (
21+
Keeper = keeper.Keeper
22+
)

golang/cosmos/x/vpurse/genesis.go

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package vpurse
2+
3+
import (
4+
// "fmt"
5+
6+
"fmt"
7+
8+
"github.com/Agoric/agoric-sdk/golang/cosmos/x/vpurse/types"
9+
sdk "github.com/cosmos/cosmos-sdk/types"
10+
abci "github.com/tendermint/tendermint/abci/types"
11+
)
12+
13+
func NewGenesisState() *types.GenesisState {
14+
return &types.GenesisState{
15+
BootstrapAddress: "",
16+
BootstrapValue: sdk.NewInt(0),
17+
DonationValue: sdk.NewInt(0),
18+
}
19+
}
20+
21+
func ValidateGenesis(data *types.GenesisState) error {
22+
if data == nil {
23+
return fmt.Errorf("vpurse genesis data cannot be nil")
24+
}
25+
if len(data.BootstrapAddress) > 0 {
26+
if _, err := sdk.AccAddressFromBech32(data.BootstrapAddress); err != nil {
27+
return fmt.Errorf("vpurse genesis invalid bootstrapAdddress %s: %w", data.BootstrapAddress, err)
28+
}
29+
}
30+
if data.BootstrapValue.IsNil() {
31+
return fmt.Errorf("vpurse genesis bootstrapValue cannot be nil")
32+
}
33+
if data.BootstrapValue.IsNegative() {
34+
return fmt.Errorf("vpurse genesis bootstrapValue %s cannot be negative", data.DonationValue.String())
35+
}
36+
if data.DonationValue.IsNil() {
37+
return fmt.Errorf("vpurse genesis donationValue cannot be nil")
38+
}
39+
if data.DonationValue.IsNegative() {
40+
return fmt.Errorf("vpurse genesis donationValue %s cannot be negative", data.DonationValue.String())
41+
}
42+
return nil
43+
}
44+
45+
func DefaultGenesisState() *types.GenesisState {
46+
gs := NewGenesisState()
47+
fmt.Println("default gen", gs)
48+
return gs
49+
}
50+
51+
func InitGenesis(ctx sdk.Context, keeper Keeper, data *types.GenesisState) []abci.ValidatorUpdate {
52+
keeper.SetGenesis(ctx, *data)
53+
fmt.Println("initialising gen", *data)
54+
return []abci.ValidatorUpdate{}
55+
}
56+
57+
func ExportGenesis(ctx sdk.Context, k Keeper) *types.GenesisState {
58+
gs := k.GetGenesis(ctx)
59+
fmt.Println("exporting gen", gs)
60+
return &gs
61+
}

golang/cosmos/x/vpurse/handler.go

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package vpurse
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/Agoric/agoric-sdk/golang/cosmos/x/swingset"
7+
8+
sdk "github.com/cosmos/cosmos-sdk/types"
9+
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
10+
)
11+
12+
// NewHandler returns a handler for "vpurse" type messages.
13+
func NewHandler(keeper Keeper) sdk.Handler {
14+
return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
15+
if swingset.IsSimulation(ctx) {
16+
// We don't support simulation.
17+
return &sdk.Result{}, nil
18+
} else {
19+
// The simulation was done, so now allow infinite gas.
20+
ctx = ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
21+
}
22+
23+
switch msg := msg.(type) {
24+
default:
25+
errMsg := fmt.Sprintf("Unrecognized vpurse Msg type: %v", msg.Type())
26+
return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, errMsg)
27+
}
28+
}
29+
}
+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package keeper
2+
3+
import (
4+
"github.com/cosmos/cosmos-sdk/codec"
5+
sdk "github.com/cosmos/cosmos-sdk/types"
6+
7+
bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
8+
9+
"github.com/Agoric/agoric-sdk/golang/cosmos/x/vpurse/types"
10+
)
11+
12+
const genesis string = "genesis"
13+
14+
// Keeper maintains the link to data storage and exposes getter/setter methods for the various parts of the state machine
15+
type Keeper struct {
16+
storeKey sdk.StoreKey
17+
cdc codec.Marshaler
18+
19+
bankKeeper bankkeeper.Keeper
20+
21+
// CallToController dispatches a message to the controlling process
22+
CallToController func(ctx sdk.Context, str string) (string, error)
23+
}
24+
25+
// NewKeeper creates a new vpurse Keeper instance
26+
func NewKeeper(
27+
cdc codec.Marshaler, key sdk.StoreKey,
28+
bankKeeper bankkeeper.Keeper,
29+
callToController func(ctx sdk.Context, str string) (string, error),
30+
) Keeper {
31+
32+
return Keeper{
33+
storeKey: key,
34+
cdc: cdc,
35+
bankKeeper: bankKeeper,
36+
CallToController: callToController,
37+
}
38+
}
39+
40+
func (k Keeper) GetGenesis(ctx sdk.Context) types.GenesisState {
41+
store := ctx.KVStore(k.storeKey)
42+
bz := store.Get([]byte(genesis))
43+
var gs types.GenesisState
44+
k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &gs)
45+
return gs
46+
}
47+
48+
func (k Keeper) SetGenesis(ctx sdk.Context, data types.GenesisState) {
49+
store := ctx.KVStore(k.storeKey)
50+
store.Set([]byte(genesis), k.cdc.MustMarshalBinaryLengthPrefixed(&data))
51+
}
52+
53+
func (k Keeper) GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin {
54+
return k.bankKeeper.GetBalance(ctx, addr, denom)
55+
}
56+
57+
func (k Keeper) SendCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error {
58+
if err := k.bankKeeper.MintCoins(ctx, types.ModuleName, amt); err != nil {
59+
return err
60+
}
61+
return k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, addr, amt)
62+
}
63+
64+
func (k Keeper) GrabCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error {
65+
if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, addr, types.ModuleName, amt); err != nil {
66+
return err
67+
}
68+
return k.bankKeeper.BurnCoins(ctx, types.ModuleName, amt)
69+
}

0 commit comments

Comments
 (0)