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

Tests reproducing problems with payments + fix #185

Merged
merged 6 commits into from
Jul 25, 2024
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
1 change: 1 addition & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ jobs:
Golem.Tests/tests/*.log
Golem.Tests/tests/*/modules/golem-data/yagna/*.log
Golem.Tests/tests/*/modules/golem-data/provider/*.log
Golem.Tests/tests/*/modules/golem-data/provider/*.json
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we use it? If so, where?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to use it because sometimes I see tests failing on CI due to empty globals.json file and want to check

Golem.Tests/tests/*/modules/golem-data/provider/exe-unit/work/logs/*.log
Golem.Tests/tests/*/modules/golem-data/provider/exe-unit/work/*/agreement.json
Golem.Tests/tests/*/modules/golem-data/provider/exe-unit/work/*/*/*.log
Expand Down
104 changes: 104 additions & 0 deletions GolemLib.Tests/TypesTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ namespace GolemLib.Tests;
// The following tests ensure that C# code's behavior matches Rust's one.
public class TypesTests
{

[Fact]
public void GolemUsage_Reward_HappyPath()
{
Expand Down Expand Up @@ -152,4 +153,107 @@ public void GolemUsage_Reward_Overflow()
var reward = usage.Reward(price);
Assert.Equal(0.088189239176m, reward);
}

public static GolemUsage UsageFromAgreement(decimal[] array)
{
return new GolemUsage(new GolemPrice
{
StartPrice = 1.0m,
EnvPerSec = array[1],
GpuPerSec = array[2],
NumRequests = array[0]
});
}

public static GolemPrice PriceFromAgreement(decimal[] array)
{
return new GolemPrice
{
StartPrice = array[3],
EnvPerSec = array[1],
GpuPerSec = array[2],
NumRequests = array[0]
};
}


[Fact]
public void GolemUsage_Reward_Case1()
{
var usage = UsageFromAgreement(new decimal[] { 27.0m, 538.8576082m, 8.7264478m });
var price = PriceFromAgreement(new decimal[] { 0.0m, 0.0004m, 0.0003m, 0.0002m });

var reward = usage.Reward(price);
Assert.Equal(0.2183609776200000003m, reward);
}

[Fact]
public void GolemUsage_Reward_Case2()
{
var usage = UsageFromAgreement(new decimal[] { 0.0m, 9.119924900000001m, 0.0m });
var price = PriceFromAgreement(new decimal[] { 0.0m, 0.0004m, 0.0003m, 0.0002m });

var reward = usage.Reward(price);
Assert.Equal(0.0038479699600000004m, reward);
}

[Fact]
public void GolemUsage_Reward_Case3()
{
var usage = UsageFromAgreement(new decimal[] { 0.0m, 9.1072277m, 0.0m });
var price = PriceFromAgreement(new decimal[] { 0.0m, 0.0004m, 0.0003m, 0.0002m });

var reward = usage.Reward(price);
Assert.Equal(0.0038428910799999996m, reward);
}

[Fact]
public void GolemUsage_Reward_Case4()
{
var usage = UsageFromAgreement(new decimal[] { 31.0m, 474.6873272m, 9.761029m });
var price = PriceFromAgreement(new decimal[] { 0.0m, 0.0003m, 0.0005m, 0.0m });

var reward = usage.Reward(price);
Assert.Equal(0.1472867126600000005m, reward);
}

[Fact]
public void GolemUsage_Reward_Case5()
{
var usage = UsageFromAgreement(new decimal[] { 0.0m, 66.5695986m, 0.0m });
var price = PriceFromAgreement(new decimal[] { 0.0m, 0.00025m, 0.00025m, 0.0m });

var reward = usage.Reward(price);
Assert.Equal(0.0166423996500000025m, reward);
}

[Fact]
public void GolemUsage_Reward_Case6()
{
var usage = UsageFromAgreement(new decimal[] { 1.0m, 41.4281323m, 9.8315089m });
var price = PriceFromAgreement(new decimal[] { 0.0m, 0.00025m, 0.00025m, 0.0m });

var reward = usage.Reward(price);
Assert.Equal(0.01281491029999999975m, reward);
}

[Fact]
public void GolemUsage_Reward_Case7()
{
var usage = UsageFromAgreement(new decimal[] { 0.0m, 9.132793m, 0.0m });
var price = PriceFromAgreement(new decimal[] { 0.0m, 0.00025m, 0.00025m, 0.0m });

var reward = usage.Reward(price);
Assert.Equal(0.00228319824999999975m, reward);
}

[Fact]
public void GolemUsage_Reward_Case8()
{
var usage = UsageFromAgreement(new decimal[] { 0.0m, 88.9031127m, 0.0m });
var price = PriceFromAgreement(new decimal[] { 0.0m, 0.00025m, 0.00025m, 0.0m });

var reward = usage.Reward(price);
Assert.Equal(0.0222257781749999975m, reward);
}
}
14 changes: 10 additions & 4 deletions GolemLib/types.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ public class GolemUsage : GolemPrice
{
public static decimal Round(decimal v) => GolemUsage.RustCompatibilityRound(v);

// Print to string with exponential notation including 16 significant digits (1 integer digit and 15 fractional digits).
private const string print16SignificantDigitsFormat = "E15";

// I couldn't find definite answer if C#'s `double` is IEEE-754 compliant floating number:
// https://csharpindepth.com/Articles/FloatingPoint claims that it is, stackoverflow claimed that it isn't.
// Still, both `double` and IEEE-754 64-bit floating numbers use 52 bits for binary significant digits,
Expand All @@ -22,11 +25,14 @@ public class GolemUsage : GolemPrice
// digits when parsing floats. In order to get the same results in Rust and C#, we need to match its behavior.
private static decimal RustCompatibilityRound(decimal i)
{
// `decimal` constructor using `double` argument truncates the value to 15 significant digits.
// To receive a more accurate result (needed to match Rust's behavior), we need to use the `string`-based construction method.
// `decimal` constructor using `double` argument truncates the value to 15 decimal significant digits.
// `decimal` to double cast also truncates the argument to 15 decimal significant digits.
// To receive a more accurate result (needed to match Rust's behavior), we need to use the `string`-based construction methods.

// Print to string with exponential notation including 16 significant digits (1 integer digit and 15 fractional digits).
var formattedDouble = ((double)i).ToString("E15", CultureInfo.InvariantCulture);
string formattedDecimal = i.ToString(print16SignificantDigitsFormat, CultureInfo.InvariantCulture);
// We need to go through a `double` to introduce float inaccuracies to match Rust's behavior.
double doubleIntroducingInaccuracy = double.Parse(formattedDecimal);
string formattedDouble = doubleIntroducingInaccuracy.ToString(print16SignificantDigitsFormat, CultureInfo.InvariantCulture);
return decimal.Parse(formattedDouble, NumberStyles.Float);
}

Expand Down
Loading