gpu-compute-chain/x/colinearcore/keeper/msg_server_new_auction.go

178 lines
5.2 KiB
Go

package keeper
import (
"context"
"encoding/json"
"errors"
"fmt"
"math/big"
"strconv"
"colinear/x/colinearcore/memdb"
"colinear/x/colinearcore/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)
func (k msgServer) NewAuction(goCtx context.Context, msg *types.MsgNewAuction) (*types.MsgNewAuctionResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
next, found := k.Keeper.GetNextAuction(ctx)
if !found {
return nil, errors.New("unable to get next auction index")
}
index := strconv.FormatUint(next.AuctionId, 10)
auctionLen := msg.LeaseEnd - uint64(ctx.BlockTime().Unix())
// check that submitted denom is allowed
for _, denom := range k.GetParams(ctx).AllowedAuctionDenoms {
if msg.Denom == denom {
goto found
}
}
return nil, fmt.Errorf("denom %s is not allowed; must be in %v", msg.Denom, k.GetParams(ctx).AllowedAuctionDenoms)
found:
if uint32(auctionLen) < k.GetParams(ctx).MinLeasePeriod {
return nil, fmt.Errorf(
"Auction length %d is below min lease period of %d",
auctionLen,
k.GetParams(ctx).MinLeasePeriod,
)
}
if uint32(auctionLen) > k.GetParams(ctx).MaxLeasePeriod {
return nil, fmt.Errorf(
"Auction length %d is above max lease period of %d",
auctionLen,
k.GetParams(ctx).MaxLeasePeriod,
)
}
senderAddr, err := sdk.AccAddressFromBech32(msg.Creator)
if err != nil {
return nil, fmt.Errorf("sender address `%s` format invalid (bech32 required)", msg.Creator)
}
if uint32(len(msg.VerifiedProviders)) > k.GetParams(ctx).MaxVerifiedProviders {
return nil, fmt.Errorf("must submit no more than %d verified providers (got %d)", k.GetParams(ctx).MaxVerifiedProviders, len(msg.VerifiedProviders))
}
bech32Len := len("colinear") + 39
for i, provider := range msg.VerifiedProviders {
if len(provider) > bech32Len {
return nil, fmt.Errorf("verified provider address %s (#%d) must be no longer than a colinear bech32 address (%d)", provider, i, bech32Len)
}
}
if msg.VerifiedProviders != nil {
memdb.AuctionDB.SetVerifiedProviders(index, msg.VerifiedProviders)
} else {
memdb.AuctionDB.SetVerifiedProviders(index, []string{})
}
auction := types.Auction{
Index: index,
Name: msg.Name,
Description: msg.Description,
// best bid -> null
// Best: new(types.Bid),
Deadline: uint64(ctx.BlockHeight()) + uint64(k.GetParams(ctx).AuctionTime),
Denom: msg.Denom,
Owner: msg.Creator,
Ceiling: msg.Ceiling,
// lease start -> null
// instead, initialize when auction is finalized
// LeaseStart: uint64(ctx.BlockTime().Unix()),
LeaseEnd: msg.LeaseEnd,
// remaining payout -> null
// Remaining: "0",
Gpus: msg.Gpus,
CpuArch: msg.CpuArch,
CpuCores: msg.CpuCores,
MemMb: msg.MemMb,
StorageGb: msg.StorageGb,
}
// validate hardware
if err := k.ValidateAuctionHardware(ctx, auction); err != nil {
return nil, err
}
spendable := k.bank.SpendableCoins(ctx, senderAddr)
// if balance does not exceed or equal proposed auction ceiling...
ceiling := new(big.Int)
ceiling.SetString(auction.Ceiling, 10)
if spendable.AmountOf(auction.Denom).BigInt().Cmp(ceiling) == -1 {
return nil, fmt.Errorf("not enough balance to set ceiling %s%s", msg.Ceiling, auction.Denom)
}
coins := sdk.NewCoins(sdk.Coin{
Amount: sdk.NewIntFromBigInt(ceiling),
Denom: auction.Denom,
})
if err := k.Keeper.bank.SendCoinsFromAccountToModule(ctx, senderAddr, types.ModuleName, coins); err != nil {
return nil, fmt.Errorf("failed to transfer %s%s", auction.Ceiling, auction.Denom)
}
k.Keeper.SetAuction(ctx, auction)
next.AuctionId++
k.Keeper.SetNextAuction(ctx, types.NextAuction{AuctionId: next.AuctionId})
gpuList, err := json.Marshal(msg.Gpus)
if err != nil {
return nil, fmt.Errorf("failed to marshal gpu list %v", msg.Gpus)
}
ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.AuctionCreatedEventType,
sdk.NewAttribute(types.AuctionCreatedIndex, auction.Index),
sdk.NewAttribute(types.AuctionCreatedEventCreator, msg.Creator),
sdk.NewAttribute(types.AuctionCreatedCeiling, msg.Ceiling),
sdk.NewAttribute(types.AuctionCreatedCpuCores, msg.CpuArch.String()),
sdk.NewAttribute(types.AuctionCreatedCpuArch, msg.CpuArch.String()),
sdk.NewAttribute(types.AuctionCreatedGpus, string(gpuList)),
sdk.NewAttribute(types.AuctionCreatedMemMb, fmt.Sprint(msg.MemMb)),
sdk.NewAttribute(types.AuctionCreatedStorage, fmt.Sprint(msg.StorageGb)),
),
)
return &types.MsgNewAuctionResponse{
AuctionId: strconv.FormatUint(next.AuctionId, 10),
}, nil
}
func (k *Keeper) ValidateAuctionHardware(ctx sdk.Context, a types.Auction) error {
if a.Gpus == nil {
return errors.New("GPU list not found")
}
// move these to module params later
if len(a.Gpus) == 0 || len(a.Gpus) > 20 {
return fmt.Errorf("must order between 1 and 20 GPUs (got %d)", len(a.Gpus))
}
for _, gpu := range a.Gpus {
if _, ok := k.GetParams(ctx).GpuModels[gpu]; !ok {
return fmt.Errorf("GPU model %s invalid", gpu)
}
}
// move these to module params later
if a.CpuCores < 1 || a.CpuCores > 12 {
return fmt.Errorf("CPU Cores must be between 1 and 12 (got %d)", a.CpuCores)
}
// move these to module params later
if a.StorageGb < 25 || a.StorageGb > 2000 {
return fmt.Errorf("storage must be between 25GB and 2TB (got %dGB)", a.StorageGb)
}
return nil
}