Skip to content

Commit

Permalink
Merge pull request #185 from golemfactory/payment-problems-again
Browse files Browse the repository at this point in the history
Tests reproducing problems with payments + fix
  • Loading branch information
nieznanysprawiciel authored Jul 25, 2024
2 parents 51c2688 + c514e75 commit 13ce2d5
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 4 deletions.
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
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

0 comments on commit 13ce2d5

Please sign in to comment.