mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-04 18:31:41 +00:00
feat: add vault module
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/onsonr/sonr/config/dwn"
|
||||
vault "github.com/onsonr/sonr/x/vault/internal"
|
||||
"github.com/onsonr/sonr/x/vault/types"
|
||||
)
|
||||
|
||||
// AssembleVault assembles the initial vault
|
||||
func (k Keeper) AssembleVault(ctx sdk.Context, subject string, origin string) (string, int64, error) {
|
||||
_, con, err := k.DIDKeeper.NewController(ctx)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
usrKs, err := con.ExportUserKs()
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
sch, err := k.CurrentSchema(ctx)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
cnfg := vault.NewConfig(usrKs, con.SonrAddress(), con.ChainID(), sch)
|
||||
|
||||
v, err := types.NewVault(cnfg, "sonr-testnet")
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
cid, err := k.ipfsClient.Unixfs().Add(context.Background(), v.FS)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
return cid.String(), k.CalculateExpiration(ctx, time.Second*15), nil
|
||||
}
|
||||
|
||||
// CurrentSchema returns the current schema
|
||||
func (k Keeper) CurrentSchema(ctx sdk.Context) (*dwn.Schema, error) {
|
||||
p, err := k.Params.Get(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
schema := p.Schema
|
||||
return &dwn.Schema{
|
||||
Version: int(schema.Version),
|
||||
Account: schema.Account,
|
||||
Asset: schema.Asset,
|
||||
Chain: schema.Chain,
|
||||
Credential: schema.Credential,
|
||||
Jwk: schema.Jwk,
|
||||
Grant: schema.Grant,
|
||||
Keyshare: schema.Keyshare,
|
||||
PublicKey: schema.PublicKey,
|
||||
Profile: schema.Profile,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/onsonr/sonr/x/vault/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGenesis(t *testing.T) {
|
||||
f := SetupTest(t)
|
||||
|
||||
genesisState := &types.GenesisState{
|
||||
Params: types.DefaultParams(),
|
||||
|
||||
// this line is used by starport scaffolding # genesis/test/state
|
||||
}
|
||||
|
||||
f.k.InitGenesis(f.ctx, genesisState)
|
||||
|
||||
got := f.k.ExportGenesis(f.ctx)
|
||||
require.NotNil(t, got)
|
||||
|
||||
// this line is used by starport scaffolding # genesis/test/assert
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
|
||||
|
||||
"cosmossdk.io/collections"
|
||||
storetypes "cosmossdk.io/core/store"
|
||||
"cosmossdk.io/log"
|
||||
"cosmossdk.io/orm/model/ormdb"
|
||||
|
||||
"github.com/ipfs/boxo/path"
|
||||
"github.com/ipfs/kubo/client/rpc"
|
||||
apiv1 "github.com/onsonr/sonr/api/vault/v1"
|
||||
|
||||
"github.com/onsonr/sonr/x/vault/types"
|
||||
|
||||
didkeeper "github.com/onsonr/sonr/x/did/keeper"
|
||||
)
|
||||
|
||||
type Keeper struct {
|
||||
cdc codec.BinaryCodec
|
||||
|
||||
logger log.Logger
|
||||
|
||||
// state management
|
||||
Schema collections.Schema
|
||||
Params collections.Item[types.Params]
|
||||
OrmDB apiv1.StateStore
|
||||
|
||||
authority string
|
||||
|
||||
ipfsClient *rpc.HttpApi
|
||||
|
||||
DIDKeeper didkeeper.Keeper
|
||||
}
|
||||
|
||||
// NewKeeper creates a new Keeper instance
|
||||
func NewKeeper(
|
||||
cdc codec.BinaryCodec,
|
||||
storeService storetypes.KVStoreService,
|
||||
logger log.Logger,
|
||||
authority string,
|
||||
didk didkeeper.Keeper,
|
||||
) Keeper {
|
||||
logger = logger.With(log.ModuleKey, "x/"+types.ModuleName)
|
||||
|
||||
sb := collections.NewSchemaBuilder(storeService)
|
||||
|
||||
if authority == "" {
|
||||
authority = authtypes.NewModuleAddress(govtypes.ModuleName).String()
|
||||
}
|
||||
|
||||
db, err := ormdb.NewModuleDB(&types.ORMModuleSchema, ormdb.ModuleDBOptions{KVStoreService: storeService})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
store, err := apiv1.NewStateStore(db)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ipfsClient, _ := rpc.NewLocalApi()
|
||||
k := Keeper{
|
||||
cdc: cdc,
|
||||
logger: logger,
|
||||
DIDKeeper: didk,
|
||||
Params: collections.NewItem(sb, types.ParamsKey, "params", codec.CollValue[types.Params](cdc)),
|
||||
OrmDB: store,
|
||||
|
||||
ipfsClient: ipfsClient,
|
||||
authority: authority,
|
||||
}
|
||||
|
||||
schema, err := sb.Build()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
k.Schema = schema
|
||||
|
||||
return k
|
||||
}
|
||||
|
||||
func (k Keeper) Logger() log.Logger {
|
||||
return k.logger
|
||||
}
|
||||
|
||||
// InitGenesis initializes the module's state from a genesis state.
|
||||
func (k *Keeper) InitGenesis(ctx context.Context, data *types.GenesisState) error {
|
||||
// this line is used by starport scaffolding # genesis/module/init
|
||||
if err := data.Params.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return k.Params.Set(ctx, data.Params)
|
||||
}
|
||||
|
||||
// ExportGenesis exports the module's state to a genesis state.
|
||||
func (k *Keeper) ExportGenesis(ctx context.Context) *types.GenesisState {
|
||||
params, err := k.Params.Get(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// this line is used by starport scaffolding # genesis/module/export
|
||||
|
||||
return &types.GenesisState{
|
||||
Params: params,
|
||||
}
|
||||
}
|
||||
|
||||
// IPFSConnected returns true if the IPFS client is initialized
|
||||
func (c Keeper) IPFSConnected() bool {
|
||||
if c.ipfsClient == nil {
|
||||
ipfsClient, err := rpc.NewLocalApi()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
c.ipfsClient = ipfsClient
|
||||
}
|
||||
return c.ipfsClient != nil
|
||||
}
|
||||
|
||||
// CalculateExpiration calculates the expiration time for a vault
|
||||
func (k Keeper) CalculateExpiration(c sdk.Context, duration time.Duration) int64 {
|
||||
blockTime := c.BlockTime()
|
||||
avgBlockTime := float64(blockTime.Sub(blockTime).Seconds())
|
||||
return int64(duration.Seconds() / avgBlockTime)
|
||||
}
|
||||
|
||||
// HasPathInIPFS checks if a file is in the local IPFS node
|
||||
func (k Keeper) HasPathInIPFS(ctx sdk.Context, cid string) (bool, error) {
|
||||
path, err := path.NewPath(cid)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
v, err := k.ipfsClient.Unixfs().Get(ctx, path)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if v == nil {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"cosmossdk.io/log"
|
||||
storetypes "cosmossdk.io/store/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/runtime"
|
||||
"github.com/cosmos/cosmos-sdk/testutil"
|
||||
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
|
||||
authcodec "github.com/cosmos/cosmos-sdk/x/auth/codec"
|
||||
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
|
||||
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
|
||||
mintkeeper "github.com/cosmos/cosmos-sdk/x/mint/keeper"
|
||||
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
|
||||
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
|
||||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
|
||||
"cosmossdk.io/core/store"
|
||||
|
||||
didkeeper "github.com/onsonr/sonr/x/did/keeper"
|
||||
module "github.com/onsonr/sonr/x/vault"
|
||||
"github.com/onsonr/sonr/x/vault/keeper"
|
||||
"github.com/onsonr/sonr/x/vault/types"
|
||||
)
|
||||
|
||||
var maccPerms = map[string][]string{
|
||||
authtypes.FeeCollectorName: nil,
|
||||
stakingtypes.BondedPoolName: {authtypes.Burner, authtypes.Staking},
|
||||
stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking},
|
||||
minttypes.ModuleName: {authtypes.Minter},
|
||||
govtypes.ModuleName: {authtypes.Burner},
|
||||
}
|
||||
|
||||
type testFixture struct {
|
||||
suite.Suite
|
||||
|
||||
ctx sdk.Context
|
||||
k keeper.Keeper
|
||||
msgServer types.MsgServer
|
||||
queryServer types.QueryServer
|
||||
appModule *module.AppModule
|
||||
|
||||
accountkeeper authkeeper.AccountKeeper
|
||||
bankkeeper bankkeeper.BaseKeeper
|
||||
stakingKeeper *stakingkeeper.Keeper
|
||||
mintkeeper mintkeeper.Keeper
|
||||
didk didkeeper.Keeper
|
||||
|
||||
addrs []sdk.AccAddress
|
||||
govModAddr string
|
||||
}
|
||||
|
||||
func SetupTest(t *testing.T) *testFixture {
|
||||
t.Helper()
|
||||
f := new(testFixture)
|
||||
require := require.New(t)
|
||||
|
||||
// Base setup
|
||||
logger := log.NewTestLogger(t)
|
||||
encCfg := moduletestutil.MakeTestEncodingConfig()
|
||||
|
||||
f.govModAddr = authtypes.NewModuleAddress(govtypes.ModuleName).String()
|
||||
f.addrs = simtestutil.CreateIncrementalAccounts(3)
|
||||
|
||||
key := storetypes.NewKVStoreKey(types.ModuleName)
|
||||
storeService := runtime.NewKVStoreService(key)
|
||||
testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
|
||||
|
||||
f.ctx = testCtx.Ctx
|
||||
|
||||
// Register SDK modules.
|
||||
registerBaseSDKModules(f, encCfg, storeService, logger, require)
|
||||
|
||||
// Setup Keeper.
|
||||
f.k = keeper.NewKeeper(encCfg.Codec, storeService, logger, f.govModAddr, f.didk)
|
||||
f.msgServer = keeper.NewMsgServerImpl(f.k)
|
||||
f.queryServer = keeper.NewQuerier(f.k)
|
||||
f.appModule = module.NewAppModule(encCfg.Codec, f.k)
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func registerModuleInterfaces(encCfg moduletestutil.TestEncodingConfig) {
|
||||
authtypes.RegisterInterfaces(encCfg.InterfaceRegistry)
|
||||
stakingtypes.RegisterInterfaces(encCfg.InterfaceRegistry)
|
||||
|
||||
types.RegisterInterfaces(encCfg.InterfaceRegistry)
|
||||
}
|
||||
|
||||
func registerBaseSDKModules(
|
||||
f *testFixture,
|
||||
encCfg moduletestutil.TestEncodingConfig,
|
||||
storeService store.KVStoreService,
|
||||
logger log.Logger,
|
||||
require *require.Assertions,
|
||||
) {
|
||||
registerModuleInterfaces(encCfg)
|
||||
|
||||
// Auth Keeper.
|
||||
f.accountkeeper = authkeeper.NewAccountKeeper(
|
||||
encCfg.Codec, storeService,
|
||||
authtypes.ProtoBaseAccount,
|
||||
maccPerms,
|
||||
authcodec.NewBech32Codec(sdk.Bech32MainPrefix), sdk.Bech32MainPrefix,
|
||||
f.govModAddr,
|
||||
)
|
||||
|
||||
// Bank Keeper.
|
||||
f.bankkeeper = bankkeeper.NewBaseKeeper(
|
||||
encCfg.Codec, storeService,
|
||||
f.accountkeeper,
|
||||
nil,
|
||||
f.govModAddr, logger,
|
||||
)
|
||||
|
||||
// Staking Keeper.
|
||||
f.stakingKeeper = stakingkeeper.NewKeeper(
|
||||
encCfg.Codec, storeService,
|
||||
f.accountkeeper, f.bankkeeper, f.govModAddr,
|
||||
authcodec.NewBech32Codec(sdk.Bech32PrefixValAddr),
|
||||
authcodec.NewBech32Codec(sdk.Bech32PrefixConsAddr),
|
||||
)
|
||||
require.NoError(f.stakingKeeper.SetParams(f.ctx, stakingtypes.DefaultParams()))
|
||||
f.accountkeeper.SetModuleAccount(f.ctx, f.stakingKeeper.GetNotBondedPool(f.ctx))
|
||||
f.accountkeeper.SetModuleAccount(f.ctx, f.stakingKeeper.GetBondedPool(f.ctx))
|
||||
|
||||
// Mint Keeper.
|
||||
f.mintkeeper = mintkeeper.NewKeeper(
|
||||
encCfg.Codec, storeService,
|
||||
f.stakingKeeper, f.accountkeeper, f.bankkeeper,
|
||||
authtypes.FeeCollectorName, f.govModAddr,
|
||||
)
|
||||
f.accountkeeper.SetModuleAccount(f.ctx, f.accountkeeper.GetModuleAccount(f.ctx, minttypes.ModuleName))
|
||||
f.mintkeeper.InitGenesis(f.ctx, f.accountkeeper, minttypes.DefaultGenesisState())
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
apiv1 "github.com/onsonr/sonr/api/vault/v1"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestORM(t *testing.T) {
|
||||
f := SetupTest(t)
|
||||
|
||||
dt := f.k.OrmDB.DWNTable()
|
||||
acc := []byte("test_acc")
|
||||
amt := uint64(7)
|
||||
|
||||
err := dt.Insert(f.ctx, &apiv1.DWN{
|
||||
Account: acc,
|
||||
Amount: amt,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
d, err := dt.Has(f.ctx, []byte("test_acc"))
|
||||
require.NoError(t, err)
|
||||
require.True(t, d)
|
||||
|
||||
res, err := dt.Get(f.ctx, []byte("test_acc"))
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res)
|
||||
require.EqualValues(t, amt, res.Amount)
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
"github.com/onsonr/sonr/x/vault/types"
|
||||
)
|
||||
|
||||
var _ types.QueryServer = Querier{}
|
||||
|
||||
type Querier struct {
|
||||
Keeper
|
||||
}
|
||||
|
||||
func NewQuerier(keeper Keeper) Querier {
|
||||
return Querier{Keeper: keeper}
|
||||
}
|
||||
|
||||
func (k Querier) Params(c context.Context, req *types.QueryParamsRequest) (*types.QueryParamsResponse, error) {
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
|
||||
p, err := k.Keeper.Params.Get(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.QueryParamsResponse{Params: &p}, nil
|
||||
}
|
||||
|
||||
// Sync implements types.QueryServer.
|
||||
func (k Querier) Sync(goCtx context.Context, req *types.SyncRequest) (*types.SyncResponse, error) {
|
||||
// ctx := sdk.UnwrapSDKContext(goCtx)
|
||||
return &types.SyncResponse{}, nil
|
||||
}
|
||||
|
||||
// BuildTx implements types.QueryServer.
|
||||
func (k Querier) BuildTx(goCtx context.Context, req *types.BuildTxRequest) (*types.BuildTxResponse, error) {
|
||||
// ctx := sdk.UnwrapSDKContext(goCtx)
|
||||
return &types.BuildTxResponse{}, nil
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
|
||||
|
||||
"cosmossdk.io/errors"
|
||||
"github.com/onsonr/sonr/x/vault/types"
|
||||
)
|
||||
|
||||
type msgServer struct {
|
||||
k Keeper
|
||||
}
|
||||
|
||||
var _ types.MsgServer = msgServer{}
|
||||
|
||||
// NewMsgServerImpl returns an implementation of the module MsgServer interface.
|
||||
func NewMsgServerImpl(keeper Keeper) types.MsgServer {
|
||||
return &msgServer{k: keeper}
|
||||
}
|
||||
|
||||
func (ms msgServer) UpdateParams(ctx context.Context, msg *types.MsgUpdateParams) (*types.MsgUpdateParamsResponse, error) {
|
||||
if ms.k.authority != msg.Authority {
|
||||
return nil, errors.Wrapf(govtypes.ErrInvalidSigner, "invalid authority; expected %s, got %s", ms.k.authority, msg.Authority)
|
||||
}
|
||||
|
||||
return nil, ms.k.Params.Set(ctx, msg.Params)
|
||||
}
|
||||
|
||||
// AllocateVault implements types.MsgServer.
|
||||
func (ms msgServer) AllocateVault(goCtx context.Context, msg *types.MsgAllocateVault) (*types.MsgAllocateVaultResponse, error) {
|
||||
// 1.Check if the service origin is valid
|
||||
ctx := sdk.UnwrapSDKContext(goCtx)
|
||||
|
||||
// 2.Allocate the vault
|
||||
cid, expiryBlock, err := ms.k.AssembleVault(ctx, msg.GetSubject(), msg.GetOrigin())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 3.Return the response
|
||||
return &types.MsgAllocateVaultResponse{
|
||||
ExpiryBlock: expiryBlock,
|
||||
Cid: cid,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/onsonr/sonr/x/vault/types"
|
||||
)
|
||||
|
||||
func TestParams(t *testing.T) {
|
||||
f := SetupTest(t)
|
||||
require := require.New(t)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
request *types.MsgUpdateParams
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
name: "fail; invalid authority",
|
||||
request: &types.MsgUpdateParams{
|
||||
Authority: f.addrs[0].String(),
|
||||
Params: types.DefaultParams(),
|
||||
},
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
name: "success",
|
||||
request: &types.MsgUpdateParams{
|
||||
Authority: f.govModAddr,
|
||||
Params: types.DefaultParams(),
|
||||
},
|
||||
err: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
_, err := f.msgServer.UpdateParams(f.ctx, tc.request)
|
||||
|
||||
if tc.err {
|
||||
require.Error(err)
|
||||
} else {
|
||||
require.NoError(err)
|
||||
|
||||
r, err := f.queryServer.Params(f.ctx, &types.QueryParamsRequest{})
|
||||
require.NoError(err)
|
||||
|
||||
require.EqualValues(&tc.request.Params, r.Params)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user