Skip to content
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
50 changes: 25 additions & 25 deletions pkg/ride/functions.gen.go

Large diffs are not rendered by default.

32 changes: 20 additions & 12 deletions pkg/ride/generate/internal/functions_generation.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,10 +298,10 @@ func catalogueV3() map[string]int {
m["@extrNative(1051)"] = 100
m["@extrNative(1052)"] = 100
m["@extrNative(1053)"] = 100
m["@extrUser(getInteger)"] = 10
m["@extrUser(getBoolean)"] = 10
m["@extrUser(getBinary)"] = 10
m["@extrUser(getString)"] = 10
m["@extrUser(getInteger)"] = 30
m["@extrUser(getBoolean)"] = 30
m["@extrUser(getBinary)"] = 30
m["@extrUser(getString)"] = 30
m["@extrUser(addressFromString)"] = 124
m["parseIntValue"] = 20
m["value"] = 13
Expand Down Expand Up @@ -528,6 +528,14 @@ func catalogueV4() map[string]int {
m["@extrNative(1052)"] = 10
m["@extrNative(1053)"] = 10
m["@extrNative(1062)"] = 1
m["getInteger"] = 4
m["getBoolean"] = 4
m["getBinary"] = 4
m["getString"] = 4
m["@extrUser(getInteger)"] = 4
m["@extrUser(getBoolean)"] = 4
m["@extrUser(getBinary)"] = 4
m["@extrUser(getString)"] = 4

m["contains"] = 3
m["containsElement"] = 5
Expand Down Expand Up @@ -957,14 +965,14 @@ func catalogueV9() map[string]int {
m["1214"] = 2 // New function ReplaceFirst
m["1215"] = 2 // New function ReplaceAll

m["getInteger"] = 7
m["getBoolean"] = 7
m["getBinary"] = 7
m["getString"] = 7
m["@extrUser(getInteger)"] = 14
m["@extrUser(getBoolean)"] = 14
m["@extrUser(getBinary)"] = 14
m["@extrUser(getString)"] = 14
m["getInteger"] = 2
m["getBoolean"] = 2
m["getBinary"] = 2
m["getString"] = 2
m["@extrUser(getInteger)"] = 2
m["@extrUser(getBoolean)"] = 2
m["@extrUser(getBinary)"] = 2
m["@extrUser(getString)"] = 2

constructorsCatalogue(ast.LibV9, m)
return m
Expand Down
24 changes: 24 additions & 0 deletions pkg/ride/testData/complexity_v3_get_value_by_index.ride
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{-# STDLIB_VERSION 3 #-}
{-# CONTENT_TYPE EXPRESSION #-}
{-# SCRIPT_TYPE ACCOUNT #-}

# Functions under test: getIntegerValue(List[DataEntry], Int)
# getBooleanValue(List[DataEntry], Int)
# getBinaryValue(List[DataEntry], Int)
# getStringValue(List[DataEntry], Int)
#
# Discrepancy (by-index throwing variants, V3 only):
# Scala doc complexity = 30 each, Go catalogue (@extrUser) = 10 each
#
# Each function is called exactly once.
# The total delta between Scala and Go estimation = 4 * (30 - 10) = 80.

let iData = [DataEntry("k", 1)]
let bData = [DataEntry("k", true)]
let nData = [DataEntry("k", base58'')]
let sData = [DataEntry("k", "s")]

getIntegerValue(iData, 0) == 1 &&
getBooleanValue(bData, 0) &&
size(getBinaryValue(nData, 0)) == 0 &&
getStringValue(sData, 0) == "s"
33 changes: 33 additions & 0 deletions pkg/ride/testData/complexity_v4_get_by_index.ride
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{-# STDLIB_VERSION 4 #-}
{-# CONTENT_TYPE EXPRESSION #-}
{-# SCRIPT_TYPE ACCOUNT #-}

# Functions under test (by-index variants, applies to STDLIB_VERSION 4-8):
#
# Option variants: getInteger / getBoolean / getBinary / getString (List, Int)
# Scala doc complexity = 4 each
# Go key: "getInteger", "getBoolean", "getBinary", "getString"
#
# Throwing variants: getIntegerValue / getBooleanValue / getBinaryValue / getStringValue (List, Int)
# Scala doc complexity = 4 each
# Go key: "@extrUser(getInteger)", "@extrUser(getBoolean)", "@extrUser(getBinary)", "@extrUser(getString)"
#
# Each of the 8 functions is called exactly once: 4 * 8 = 32.
# IsDefined costs 1 each, equality checks cost 1 each, size check costs 1: 1 * 4 = 4
# Lists creation is counted as 4*(1+2) = 12.
# Additionally, the script contains 3 equality checks and 1 size check: 3 * 1 + 1 = 4.
# Total estimation complexity: 32 + 4 + 12 + 4 = 52.

let iData = [IntegerEntry("k", 1)] # 1+2 - 1 for List creation and 2 for *Entry constructor.
let bData = [BooleanEntry("k", true)] # 1+2
let nData = [BinaryEntry("k", base58'')] # 1+2
let sData = [StringEntry("k", "s")] # 1+2

isDefined(getInteger(iData, 0)) && # 1+4 - 1 for isDefined and 4 for getInteger.
isDefined(getBoolean(bData, 0)) && # 1+4
isDefined(getBinary(nData, 0)) && # 1+4
isDefined(getString(sData, 0)) && # 1+4
getIntegerValue(iData, 0) == 1 && # 4+1 - 4 for getIntegerValue and 1 for equality check.
getBooleanValue(bData, 0) && # 4
size(getBinaryValue(nData, 0)) == 0 && # 1+4+1 - 1 for size, 4 for getBinaryValue, 1 for equality check.
getStringValue(sData, 0) == "s" # 4+1
13 changes: 13 additions & 0 deletions pkg/ride/testData/complexity_v6_address_from_public_key.ride
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{-# STDLIB_VERSION 6 #-}
{-# CONTENT_TYPE EXPRESSION #-}
{-# SCRIPT_TYPE ACCOUNT #-}

# Function under test: addressFromPublicKey(ByteVector)
# Applies to STDLIB_VERSION 6-9.
#
# In STDLIB_VERSION >= 6, addressFromPublicKey is compiled as native id 1063 (Go catalogue cost = 1),
# matching Scala doc complexity = 1.
#
# Function addressFromPublicKey is called once.

addressFromPublicKey(tx.senderPublicKey) == this
42 changes: 42 additions & 0 deletions pkg/ride/testData/complexity_v9_get_by_index.ride
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{-# STDLIB_VERSION 9 #-}
{-# CONTENT_TYPE EXPRESSION #-}
{-# SCRIPT_TYPE ACCOUNT #-}

# Functions under test (by-index variants, STDLIB_VERSION 9):
#
# Option variants — getInteger / getBoolean / getBinary / getString (List, Int)
# Scala doc complexity = 2 each
# Go key: "getInteger", "getBoolean", "getBinary", "getString"
#
# Throwing variants — getIntegerValue / getBooleanValue / getBinaryValue / getStringValue (List, Int)
# Scala doc complexity = 2 each
# Go key: "@extrUser(getInteger)", "@extrUser(getBoolean)", "@extrUser(getBinary)", "@extrUser(getString)"
#
# addressFromPublicKey(ByteVector)
# Scala doc complexity = 1
#
# Each of the 8 data-getter functions is called once: 8 * 2 = 16
# Function addressFromPublicKey called once: 1
# Each list creation is counted as 1 + 2: 4 * (1 + 2) = 12
# IsDefined called 4 times: 4 * 1 = 4
# Estimation comparisons: 4 equality checks (4) + 1 inequality check (1) = 5 (from `addr == this || addr != this`)
# Evaluation comparisons: short-circuited `||`, so only 4 equality checks are executed
# One size call: 1
# Total Estimation Complexity: 16 + 1 + 12 + 4 + 5 + 1 = 39.
# Total Evaluation Complexity: 16 + 1 + 8 + 4 + 4 + 1 = 34.

let iData = [IntegerEntry("k", 1)] # EvC: 1 + 1 = 2, EsC: 1 + 2 = 3
let bData = [BooleanEntry("k", true)] # EvC: 2, EsC: 3
let nData = [BinaryEntry("k", base58'')] # EvC: 2, EsC: 3
let sData = [StringEntry("k", "s")] # EvC: 2, EsC: 3
let addr = addressFromPublicKey(tx.senderPublicKey) # EvC: 1, EsC: 1

isDefined(getInteger(iData, 0)) && # 1 + 2
isDefined(getBoolean(bData, 0)) && # 1 + 2
isDefined(getBinary(nData, 0)) && # 1 + 2
isDefined(getString(sData, 0)) && # 1 + 2
getIntegerValue(iData, 0) == 1 && # 1 + 2
getBooleanValue(bData, 0) && # 2
size(getBinaryValue(nData, 0)) == 0 && # 1 + 2 + 1
getStringValue(sData, 0) == "s" && # 1 + 2
(addr == this || addr != this) # EvC: 1 (only one branch is evaluated), EsC: 1 + 1
12 changes: 6 additions & 6 deletions pkg/ride/tree_estimation.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ func EstimateTree(tree *ast.Tree, v int) (TreeEstimation, error) {
if err != nil {
return TreeEstimation{}, errors.Wrapf(err, "failed to estimate with tree estimator V%d", v)
}
max, verifier, functions, err := te.estimate()
e, verifier, functions, err := te.estimate()
if err != nil {
return TreeEstimation{}, errors.Wrapf(err, "failed to estimate with tree estimator V%d", v)
}
return TreeEstimation{Estimation: max, Verifier: verifier, Functions: functions}, nil
return TreeEstimation{Estimation: e, Verifier: verifier, Functions: functions}, nil
case 2:
id := base64.StdEncoding.EncodeToString(tree.Digest[:])
switch id {
Expand All @@ -50,11 +50,11 @@ func EstimateTree(tree *ast.Tree, v int) (TreeEstimation, error) {
if err != nil {
return TreeEstimation{}, errors.Wrapf(err, "failed to estimate with tree estimator V%d", v)
}
max, verifier, functions, err := te.estimate()
e, verifier, functions, err := te.estimate()
if err != nil {
return TreeEstimation{}, errors.Wrapf(err, "failed to estimate with tree estimator V%d", v)
}
return TreeEstimation{Estimation: max, Verifier: verifier, Functions: functions}, nil
return TreeEstimation{Estimation: e, Verifier: verifier, Functions: functions}, nil
case 3:
te, err := newTreeEstimatorV3(tree)
if err != nil {
Expand All @@ -70,11 +70,11 @@ func EstimateTree(tree *ast.Tree, v int) (TreeEstimation, error) {
if err != nil {
return TreeEstimation{}, errors.Wrapf(err, "failed to estimate with tree estimator V%d", v)
}
max, verifier, functions, err := te.estimate()
e, verifier, functions, err := te.estimate()
if err != nil {
return TreeEstimation{}, errors.Wrapf(err, "failed to estimate with tree estimator V%d", v)
}
return TreeEstimation{Estimation: max, Verifier: verifier, Functions: functions}, nil
return TreeEstimation{Estimation: e, Verifier: verifier, Functions: functions}, nil
default:
return TreeEstimation{}, errors.Errorf("unsupported version of tree estimator '%d'", v)
}
Expand Down
31 changes: 31 additions & 0 deletions pkg/ride/tree_estimation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"encoding/base64"
stderrs "errors"
"fmt"
"io"
"os"
"testing"
"time"

Expand Down Expand Up @@ -678,3 +680,32 @@ func TestRideV9ScalaCompatibility(t *testing.T) {
assert.Equal(t, 51, est.Functions["addAdditionalRewards"])
assert.Equal(t, 36280, est.Functions["distributeTokens"])
}

func TestUserFunctionEstimation(t *testing.T) {
for _, test := range []struct {
fn string
estimatorVersion int
expectedComplexity int
}{
{fn: "testData/complexity_v3_get_value_by_index.ride", estimatorVersion: 4, expectedComplexity: 140},
{fn: "testData/complexity_v4_get_by_index.ride", estimatorVersion: 4, expectedComplexity: 52},
{fn: "testData/complexity_v6_address_from_public_key.ride", estimatorVersion: 4, expectedComplexity: 2},
{fn: "testData/complexity_v9_get_by_index.ride", estimatorVersion: 4, expectedComplexity: 39},
} {
t.Run(fmt.Sprintf("Estimation test '%s' with estimatorV%d", test.fn, test.estimatorVersion),
func(t *testing.T) {
f, err := os.Open(test.fn)
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, f.Close()) })
src, err := io.ReadAll(f)
require.NoError(t, err)
code := string(src)
tree, errs := ridec.CompileToTree(code)
require.Empty(t, errs)
e, err := EstimateTree(tree, test.estimatorVersion)
require.NoError(t, err)
assert.Equal(t, test.expectedComplexity, e.Estimation)
assert.Equal(t, test.expectedComplexity, e.Verifier)
})
}
}
39 changes: 39 additions & 0 deletions pkg/ride/tree_evaluation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"encoding/json"
stderrs "errors"
"fmt"
"io"
"os"
"strings"
"testing"

Expand Down Expand Up @@ -6463,3 +6465,40 @@ func TestCallVerifierWithThrow(t *testing.T) {
assert.Equal(t, UserError, GetEvaluationErrorType(err))
})
}

func TestUserFunctionEvaluationComplexities(t *testing.T) {
dApp1 := newTestAccount(t, "DAPP1") // 3MzDtgL5yw73C2xVLnLJCrT5gCL4357a4sz

for _, test := range []struct {
fn string
expectedComplexity int
}{
{fn: "testData/complexity_v4_get_by_index.ride", expectedComplexity: 48},
{fn: "testData/complexity_v6_address_from_public_key.ride", expectedComplexity: 2},
{fn: "testData/complexity_v9_get_by_index.ride", expectedComplexity: 34},
} {
const complexityLimit = 1000
t.Run(fmt.Sprintf("Evaluation test '%s'", test.fn),
func(t *testing.T) {
f, err := os.Open(test.fn)
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, f.Close()) })
src, err := io.ReadAll(f)
require.NoError(t, err)
code := string(src)
tree, errs := ridec.CompileToTree(code)
require.Empty(t, errs)

env := newTestEnv(t).withLibVersion(tree.LibVersion).withComplexityLimit(complexityLimit).
withBlockV5Activated().withProtobufTx().withRideV6Activated().
withDataEntriesSizeV2().withMessageLengthV3().withValidateInternalPayments().
withThis(dApp1).withDApp(dApp1).withSender(dApp1).
withInvocation("verify", withTransactionID(crypto.Digest{})).withTree(dApp1, tree).
withWrappedState()

res, err := CallVerifier(env.toEnv(), tree)
require.NoError(t, err)
assert.Equal(t, test.expectedComplexity, res.Complexity())
})
}
}
Loading