Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Bid pricing based on USD scale #1385

Merged
merged 7 commits into from
Oct 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 37 additions & 2 deletions provider/bidengine/pricing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,26 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/shopspring/decimal"
io "io"
"math"
"math/big"
"net/http"
"net/http/httptest"
"os"
"os/exec"
"path"
"path/filepath"
"testing"
"time"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/shopspring/decimal"
"github.com/stretchr/testify/require"

"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"
)

func Test_ScalePricingRejectsAllZero(t *testing.T) {
Expand Down Expand Up @@ -545,6 +549,37 @@ func Test_ScriptPricingWritesJsonToStdin(t *testing.T) {
}
}

func Test_ScriptPricingFromScript(t *testing.T) {
const (
mockAPIResponse = `{"akash-network":{"usd":3.57}}`
expectedPrice = 67843138
)

server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, err := w.Write([]byte(mockAPIResponse))
require.NoError(t, err)
}))
defer server.Close()

err := os.Setenv("API_URL", server.URL)
require.NoError(t, err)

scriptPath, err := filepath.Abs("../../script/usd_pricing_oracle.sh")
require.NoError(t, err)

pricing, err := MakeShellScriptPricing(scriptPath, 1, 30000*time.Millisecond)
require.NoError(t, err)
require.NotNil(t, pricing)

gspec := defaultGroupSpec()
gspec.Resources[0].Resources.Endpoints = make([]atypes.Endpoint, 7)

price, err := pricing.CalculatePrice(context.Background(), testutil.AccAddress(t).String(),
gspec)
require.NoError(t, err)
require.True(t, price.Equal(sdk.NewCoin("uakt", sdk.NewInt(expectedPrice))))
}

func TestRationalToIntConversion(t *testing.T) {
x := ceilBigRatToBigInt(big.NewRat(0, 1))
require.Equal(t, big.NewInt(0), x)
Expand Down
99 changes: 99 additions & 0 deletions script/usd_pricing_oracle.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#!/bin/bash

# To run this script, the following commands need to be installed:
#
# * jq
# * bc
# * curl

# One can set API_URL env variable to the url that returns coingecko like response, something like: `{"akash-network":{"usd":3.57}}`
# and if the API_URL isn't set, the default api url will be used

# URL to get current USD price per AKT
DEFAULT_API_URL="https://api.coingecko.com/api/v3/simple/price?ids=akash-network&vs_currencies=usd"

# These are the variables one can modify to change the USD scale for each resource kind
CPU_USD_SCALE=0.10
MEMORY_USD_SCALE=0.02
STORAGE_USD_SCALE=0.01
ENDPOINT_USD_SCALE=0.02

# used later for validation
MAX_INT64=9223372036854775807

# local variables used for calculation
memory_total=0
cpu_total=0
storage_total=0
endpoint_total=0

# read the JSON in `stdin` into $script_input
read -r script_input

# iterate over all the groups and calculate total quantity of each resource
for group in $(jq -c '.[]' <<<"$script_input"); do
count=$(jq '.count' <<<"$group")

memory_quantity=$(jq '.memory' <<<"$group")
memory_quantity=$((memory_quantity * count))
memory_total=$((memory_total + memory_quantity))

cpu_quantity=$(jq '.cpu' <<<"$group")
cpu_quantity=$((cpu_quantity * count))
cpu_total=$((cpu_total + cpu_quantity))

storage_quantity=$(jq '.storage' <<<"$group")
storage_quantity=$((storage_quantity * count))
storage_total=$((storage_total + storage_quantity))

endpoint_quantity=$(jq '.endpoint_quantity' <<<"$group")
endpoint_quantity=$((endpoint_quantity * count))
endpoint_total=$((endpoint_total + endpoint_quantity))
done

# calculate the total cost in USD for each resource
cpu_cost_usd=$(bc -l <<<"${cpu_total}*${CPU_USD_SCALE}")
memory_cost_usd=$(bc -l <<<"${memory_total}*${MEMORY_USD_SCALE}")
storage_cost_usd=$(bc -l <<<"${storage_total}*${STORAGE_USD_SCALE}")
endpoint_cost_usd=$(bc -l <<<"${endpoint_total}*${ENDPOINT_USD_SCALE}")

# validate the USD cost for each resource
if [ 1 -eq "$(bc <<<"${cpu_cost_usd}<0")" ] || [ 0 -eq "$(bc <<<"${cpu_cost_usd}<=${MAX_INT64}")" ] ||
[ 1 -eq "$(bc <<<"${memory_cost_usd}<0")" ] || [ 0 -eq "$(bc <<<"${memory_cost_usd}<=${MAX_INT64}")" ] ||
[ 1 -eq "$(bc <<<"${storage_cost_usd}<0")" ] || [ 0 -eq "$(bc <<<"${storage_cost_usd}<=${MAX_INT64}")" ] ||
[ 1 -eq "$(bc <<<"${endpoint_cost_usd}<0")" ] || [ 0 -eq "$(bc <<<"${endpoint_cost_usd}<=${MAX_INT64}")" ]; then
exit 1
fi

# finally, calculate the total cost in USD of all resources and validate it
total_cost_usd=$(bc -l <<<"${cpu_cost_usd}+${memory_cost_usd}+${storage_cost_usd}+${endpoint_cost_usd}")
if [ 1 -eq "$(bc <<<"${total_cost_usd}<0")" ] || [ 0 -eq "$(bc <<<"${total_cost_usd}<=${MAX_INT64}")" ]; then
exit 1
fi

# call the API and find out the current USD price per AKT
if [ -z "$API_URL" ]; then
API_URL=$DEFAULT_API_URL
fi

API_RESPONSE=$(curl -s "$API_URL")
curl_exit_status=$?
if [ $curl_exit_status != 0 ]; then
exit $curl_exit_status
fi
usd_per_akt=$(jq '."akash-network"."usd"' <<<"$API_RESPONSE")

# validate the current USD price per AKT is not zero
if [ 1 -eq "$(bc <<< "${usd_per_akt}==0")" ]; then
exit 1
fi

# calculate the total cost in uAKT
total_cost_akt=$(bc -l <<<"${total_cost_usd}/${usd_per_akt}")
total_cost_uakt=$(bc -l <<<"${total_cost_akt}*1000000")

# Round upwards to get an integer
total_cost_uakt=$(echo "$total_cost_uakt" | jq '.|ceil')

# return the price in uAKT
echo "$total_cost_uakt"