Skip to content

Commit

Permalink
feat(provider): Use per megabyte for scale of storage & memory pricin…
Browse files Browse the repository at this point in the history
…g calculation
  • Loading branch information
hydrogen18 authored Nov 26, 2020
1 parent 2dcf6ff commit 9ce6b96
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 20 deletions.
50 changes: 36 additions & 14 deletions provider/bidengine/pricing.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,34 +53,48 @@ func MakeScalePricing(
var ErrBidQuantityInvalid = errors.New("A bid quantity is invalid")
var ErrBidZero = errors.New("A bid of zero was produced")

func ceilBigRatToBigInt(v *big.Rat) *big.Int {
numerator := v.Num()
denom := v.Denom()

result := big.NewInt(0).Div(numerator, denom)
if !v.IsInt() {
result.Add(result, big.NewInt(1))
}

return result
}

func (fp scalePricing) calculatePrice(ctx context.Context, gspec *dtypes.GroupSpec) (sdk.Coin, error) {
// Use unlimited precision math here.
// Otherwise a correctly crafted order could create a cost of '1' given
// a possible configuration
cpuTotal := big.NewInt(0)
memoryTotal := big.NewInt(0)
storageTotal := big.NewInt(0)
memoryTotal := big.NewRat(0, 1)
storageTotal := big.NewRat(0, 1)
endpointTotal := big.NewInt(0)

// iterate over everything & sum it up
for _, group := range gspec.Resources {

groupCount := big.NewInt(0)
groupCount.SetUint64(uint64(group.Count)) // Expand uint32 to uint64
groupCountRat := big.NewRat(0, 1)
groupCountRat.SetUint64(uint64(group.Count))

cpuQuantity := big.NewInt(0)
cpuQuantity.SetUint64(group.Resources.CPU.Units.Val.Uint64())
cpuQuantity.Mul(cpuQuantity, groupCount)
cpuTotal.Add(cpuTotal, cpuQuantity)

memoryQuantity := big.NewInt(0)
memoryQuantity := big.NewRat(0, 1)
memoryQuantity.SetUint64(group.Resources.Memory.Quantity.Value())
memoryQuantity.Mul(memoryQuantity, groupCount)
memoryQuantity.Mul(memoryQuantity, groupCountRat)
memoryTotal.Add(memoryTotal, memoryQuantity)

storageQuantity := big.NewInt(0)
storageQuantity := big.NewRat(0, 1)
storageQuantity.SetUint64(group.Resources.Storage.Quantity.Val.Uint64())
storageQuantity.Mul(storageQuantity, groupCount)
storageQuantity.Mul(storageQuantity, groupCountRat)
storageTotal.Add(storageTotal, storageQuantity)

endpointQuantity := big.NewInt(0)
Expand All @@ -92,27 +106,35 @@ func (fp scalePricing) calculatePrice(ctx context.Context, gspec *dtypes.GroupSp
scale.SetUint64(fp.cpuScale)
cpuTotal.Mul(cpuTotal, scale)

scale.SetUint64(fp.memoryScale)
memoryTotal.Mul(memoryTotal, scale)
mebibytes := big.NewRat(unit.Mi, 1)
memoryTotal.Quo(memoryTotal, mebibytes)

scaleRat := big.NewRat(0, 1)
scaleRat.SetUint64(fp.memoryScale)
memoryTotal.Mul(memoryTotal, scaleRat)

scale.SetUint64(fp.storageScale)
storageTotal.Mul(storageTotal, scale)
storageTotal.Quo(storageTotal, mebibytes)
scaleRat.SetUint64(fp.storageScale)
storageTotal.Mul(storageTotal, scaleRat)

scale.SetUint64(fp.endpointScale)
endpointTotal.Mul(endpointTotal, scale)

memoryTotalInt := ceilBigRatToBigInt(memoryTotal)
storageTotalInt := ceilBigRatToBigInt(storageTotal)

// Each quantity must be non negative
// and fit into an Int64
if cpuTotal.Sign() < 0 || !cpuTotal.IsInt64() ||
memoryTotal.Sign() < 0 || !memoryTotal.IsInt64() ||
storageTotal.Sign() < 0 || !storageTotal.IsInt64() ||
memoryTotal.Sign() < 0 || !memoryTotalInt.IsInt64() ||
storageTotal.Sign() < 0 || !storageTotalInt.IsInt64() ||
endpointTotal.Sign() < 0 || !endpointTotal.IsInt64() {
return sdk.Coin{}, ErrBidQuantityInvalid
}

cpuCost := sdk.NewCoin(denom, sdk.NewIntFromBigInt(cpuTotal))
memoryCost := sdk.NewCoin(denom, sdk.NewIntFromBigInt(memoryTotal))
storageCost := sdk.NewCoin(denom, sdk.NewIntFromBigInt(storageTotal))
memoryCost := sdk.NewCoin(denom, sdk.NewIntFromBigInt(memoryTotalInt))
storageCost := sdk.NewCoin(denom, sdk.NewIntFromBigInt(storageTotalInt))

// Check for less than or equal to zero
cost := cpuCost.Add(memoryCost).Add(storageCost)
Expand Down
77 changes: 74 additions & 3 deletions provider/bidengine/pricing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ovrclk/akash/testutil"
atypes "github.com/ovrclk/akash/types"
"github.com/ovrclk/akash/types/unit"
dtypes "github.com/ovrclk/akash/x/deployment/types"
"github.com/stretchr/testify/require"
io "io"
"math"
"math/big"
"os"
"os/exec"
"path"
Expand Down Expand Up @@ -105,7 +107,7 @@ func Test_ScalePricingOnCpu(t *testing.T) {

func Test_ScalePricingOnMemory(t *testing.T) {
memoryScale := uint64(23)
pricing, err := MakeScalePricing(0, memoryScale, 0, 0)
pricing, err := MakeScalePricing(0, memoryScale*unit.Mi, 0, 0)
require.NoError(t, err)
require.NotNil(t, pricing)

Expand All @@ -119,9 +121,64 @@ func Test_ScalePricingOnMemory(t *testing.T) {
require.NoError(t, err)
}

func Test_ScalePricingOnMemoryRoundsUpA(t *testing.T) {
memoryScale := uint64(123)
pricing, err := MakeScalePricing(0, memoryScale, 0, 0)
require.NoError(t, err)
require.NotNil(t, pricing)

gspec := defaultGroupSpec()
// Make a resource exactly 1 byte greater than a megabyte
memoryQuantity := uint64(unit.Mi + 1)
gspec.Resources[0].Resources.Memory.Quantity = atypes.NewResourceValue(memoryQuantity)
price, err := pricing.calculatePrice(context.Background(), gspec)

// The pricing function cannot round down, so the price must exactly 1 uakt larger
// than the scale provided
expectedPrice := testutil.AkashCoin(t, int64(124))
require.Equal(t, expectedPrice, price)
require.NoError(t, err)
}

func Test_ScalePricingOnMemoryRoundsUpB(t *testing.T) {
memoryScale := uint64(123)
pricing, err := MakeScalePricing(0, memoryScale, 0, 0)
require.NoError(t, err)
require.NotNil(t, pricing)

gspec := defaultGroupSpec()
// Make a resource exactly 1 less byte less than two megabytes
memoryQuantity := uint64(2*unit.Mi - 1)
gspec.Resources[0].Resources.Memory.Quantity = atypes.NewResourceValue(memoryQuantity)
price, err := pricing.calculatePrice(context.Background(), gspec)

// The pricing function cannot round down, so the price must exactly twice the scale
expectedPrice := testutil.AkashCoin(t, int64(246))
require.Equal(t, expectedPrice, price)
require.NoError(t, err)
}

func Test_ScalePricingOnMemoryRoundsUpFromZero(t *testing.T) {
memoryScale := uint64(1) // 1 uakt per megabyte
pricing, err := MakeScalePricing(0, memoryScale, 0, 0)
require.NoError(t, err)
require.NotNil(t, pricing)

gspec := defaultGroupSpec()
// Make a resource exactly 1 byte
memoryQuantity := uint64(1)
gspec.Resources[0].Resources.Memory.Quantity = atypes.NewResourceValue(memoryQuantity)
price, err := pricing.calculatePrice(context.Background(), gspec)

// The pricing function cannot round down, so the price must exactly 1 uakt
expectedPrice := testutil.AkashCoin(t, int64(1))
require.Equal(t, expectedPrice, price)
require.NoError(t, err)
}

func Test_ScalePricingOnStorage(t *testing.T) {
storageScale := uint64(24)
pricing, err := MakeScalePricing(0, 0, storageScale, 0)
pricing, err := MakeScalePricing(0, 0, storageScale*unit.Mi, 0)
require.NoError(t, err)
require.NotNil(t, pricing)

Expand All @@ -137,7 +194,7 @@ func Test_ScalePricingOnStorage(t *testing.T) {

func Test_ScalePricingByCountOfResources(t *testing.T) {
storageScale := uint64(3)
pricing, err := MakeScalePricing(0, 0, storageScale, 0)
pricing, err := MakeScalePricing(0, 0, storageScale*unit.Mi, 0)
require.NoError(t, err)
require.NotNil(t, pricing)

Expand Down Expand Up @@ -439,3 +496,17 @@ func Test_ScriptPricingWritesJsonToStdin(t *testing.T) {
require.Equal(t, len(r.Resources.Endpoints), data[i].EndpointQuantity)
}
}

func TestRationalToIntConversion(t *testing.T) {
x := ceilBigRatToBigInt(big.NewRat(0, 1))
require.Equal(t, big.NewInt(0), x)

y := ceilBigRatToBigInt(big.NewRat(1, 1))
require.Equal(t, big.NewInt(1), y)

z := ceilBigRatToBigInt(big.NewRat(1, 2))
require.Equal(t, big.NewInt(1), z)

a := ceilBigRatToBigInt(big.NewRat(3, 2))
require.Equal(t, big.NewInt(2), a)
}
6 changes: 3 additions & 3 deletions provider/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,17 +104,17 @@ func RunCmd() *cobra.Command {
return nil
}

cmd.Flags().Uint64(FlagBidPriceCPUScale, 0, "cpu pricing scale in uakt")
cmd.Flags().Uint64(FlagBidPriceCPUScale, 0, "cpu pricing scale in uakt per millicpu")
if err := viper.BindPFlag(FlagBidPriceCPUScale, cmd.Flags().Lookup(FlagBidPriceCPUScale)); err != nil {
return nil
}

cmd.Flags().Uint64(FlagBidPriceMemoryScale, 0, "memory pricing scale in uakt")
cmd.Flags().Uint64(FlagBidPriceMemoryScale, 0, "memory pricing scale in uakt per megabyte")
if err := viper.BindPFlag(FlagBidPriceMemoryScale, cmd.Flags().Lookup(FlagBidPriceMemoryScale)); err != nil {
return nil
}

cmd.Flags().Uint64(FlagBidPriceStorageScale, 0, "storage pricing scale in uakt")
cmd.Flags().Uint64(FlagBidPriceStorageScale, 0, "storage pricing scale in uakt per megabyte")
if err := viper.BindPFlag(FlagBidPriceStorageScale, cmd.Flags().Lookup(FlagBidPriceStorageScale)); err != nil {
return nil
}
Expand Down

0 comments on commit 9ce6b96

Please sign in to comment.