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

New test "Request Expires If Not Filled And Money Are Returned" #55

Draft
wants to merge 1 commit into
base: feature/marketplace-contracts
Choose a base branch
from
Draft
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
2 changes: 1 addition & 1 deletion DistTestCore/Codex/CodexContainerRecipe.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace DistTestCore.Codex
{
public class CodexContainerRecipe : ContainerRecipeFactory
{
public const string DefaultDockerImage = "codexstorage/nim-codex:sha-5141d85";
public const string DefaultDockerImage = "codexstorage/nim-codex:latest-dist-tests";
public const string MetricsPortTag = "metrics_port";
public const string DiscoveryPortTag = "discovery-port";

Expand Down
3 changes: 1 addition & 2 deletions DistTestCore/Helpers/AssertHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ public static void RetryAssert<T>(IResolveConstraint constraint, Func<T> actual,
{
try
{
var c = constraint.Resolve();
Time.WaitUntil(() => c.ApplyTo(actual()).IsSuccess);
Time.WaitUntil(() => constraint.Resolve().ApplyTo(actual()).IsSuccess);
}
catch (TimeoutException)
{
Expand Down
55 changes: 44 additions & 11 deletions DistTestCore/Marketplace/MarketplaceAccess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
using NUnit.Framework;
using NUnit.Framework.Constraints;
using System.Numerics;
using System.Linq;
using Utils;

namespace DistTestCore.Marketplace
{
public interface IMarketplaceAccess
{
string MakeStorageAvailable(ByteSize size, TestToken minPricePerBytePerSecond, TestToken maxCollateral, TimeSpan maxDuration);
StoragePurchaseContract RequestStorage(ContentId contentId, TestToken pricePerSlotPerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration);
StoragePurchaseContract RequestStorage(ContentId contentId, TestToken pricePerSlotPerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration, DateTime? expiry = null, uint? tolerance = null);
void AssertThatBalance(IResolveConstraint constraint, string message = "");
TestToken GetBalance();
}
Expand All @@ -32,25 +33,28 @@ public MarketplaceAccess(TestLifecycle lifecycle, MarketplaceNetwork marketplace
this.codexAccess = codexAccess;
}

public StoragePurchaseContract RequestStorage(ContentId contentId, TestToken pricePerSlotPerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration)
public StoragePurchaseContract RequestStorage(ContentId contentId, TestToken pricePerSlotPerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration, DateTime? expiry = null, uint? tolerance = null)
{
var expiryTimestamp = expiry is null ? null : ((DateTimeOffset)expiry).ToUnixTimeSeconds().ToString();
var request = new CodexSalesRequestStorageRequest
{
duration = ToDecInt(duration.TotalSeconds),
proofProbability = ToDecInt(proofProbability),
reward = ToDecInt(pricePerSlotPerSecond),
collateral = ToDecInt(requiredCollateral),
expiry = null,
expiry = expiryTimestamp,
nodes = minRequiredNumberOfNodes,
tolerance = null,
tolerance = tolerance,
};

Log($"Requesting storage for: {contentId.Id}... (" +
$"pricePerSlotPerSecond: {pricePerSlotPerSecond}, " +
$"requiredCollateral: {requiredCollateral}, " +
$"minRequiredNumberOfNodes: {minRequiredNumberOfNodes}, " +
$"proofProbability: {proofProbability}, " +
$"duration: {Time.FormatDuration(duration)})");
$"duration: {Time.FormatDuration(duration)}, " +
$"expiry: {expiry:yyyy-MM-dd HH:mm:ss}, " +
$"tolerance: {tolerance})");

var response = codexAccess.RequestStorage(request, contentId.Id);

Expand Down Expand Up @@ -123,7 +127,7 @@ private void Log(string msg)

public class MarketplaceUnavailable : IMarketplaceAccess
{
public StoragePurchaseContract RequestStorage(ContentId contentId, TestToken pricePerBytePerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration)
public StoragePurchaseContract RequestStorage(ContentId contentId, TestToken pricePerBytePerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration, DateTime? expiry = null, uint? tolerance = null)
{
Unavailable();
return null!;
Expand Down Expand Up @@ -187,6 +191,29 @@ public void WaitForStorageContractFinished()
WaitForStorageContractState(timeout, "finished");
}

/// <summary>
/// Wait for contract to terminate. Which means that it reaches one of the following states: Finished, Cancelled, Failed
/// </summary>
public void WaitForStorageContractTerminated()
{
if (!contractStartUtc.HasValue)
{
WaitForStorageContractStarted();
}
var gracePeriod = TimeSpan.FromSeconds(10);
var currentContractTime = DateTime.UtcNow - contractStartUtc!.Value;
var timeout = (ContractDuration - currentContractTime) + gracePeriod;
string[] terminatedStates = { "finished", "failed", "cancelled" };
WaitForStorageContractState(timeout, terminatedStates);
}

public void WaitForStorageContractFailed(TimeSpan timeout)
{
WaitForStorageContractState(timeout, "failed");
contractStartUtc = DateTime.UtcNow;
}


/// <summary>
/// Wait for contract to start. Max timeout depends on contract filesize. Allows more time for larger files.
/// </summary>
Expand All @@ -205,12 +232,18 @@ public void WaitForStorageContractStarted(TimeSpan timeout)
}

private void WaitForStorageContractState(TimeSpan timeout, string desiredState)
{
string[] states = { desiredState };
WaitForStorageContractState(timeout, states);
}

private void WaitForStorageContractState(TimeSpan timeout, string[] desiredState)
{
var lastState = "";
var waitStart = DateTime.UtcNow;
log.Log($"Waiting for {Time.FormatDuration(timeout)} for contract '{PurchaseId}' to reach state '{desiredState}'.");
while (lastState != desiredState)

log.Log($"Waiting for {Time.FormatDuration(timeout)} for contract '{PurchaseId}' to reach state '{string.Join("/", desiredState)}'.");
while (!desiredState.Contains(lastState))
{
var purchaseStatus = codexAccess.GetPurchaseStatus(PurchaseId);
var statusJson = JsonConvert.SerializeObject(purchaseStatus);
Expand All @@ -229,10 +262,10 @@ private void WaitForStorageContractState(TimeSpan timeout, string desiredState)

if (DateTime.UtcNow - waitStart > timeout)
{
Assert.Fail($"Contract did not reach '{desiredState}' within timeout. {statusJson}");
Assert.Fail($"Contract did not reach '{string.Join("/", desiredState)}' within timeout. {statusJson}");
}
}
log.Log($"Contract '{desiredState}'.");
log.Log($"Contract '{string.Join("/", desiredState)}'.");
}

public CodexStoragePurchase GetPurchaseStatus(string purchaseId)
Expand Down
5 changes: 4 additions & 1 deletion Tests/BasicTests/ExampleTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using DistTestCore;
using DistTestCore.Codex;
using NUnit.Framework;

namespace Tests.BasicTests
Expand Down Expand Up @@ -46,6 +47,7 @@ public void MarketplaceExample()
var fileSize = 10.MB();

var seller = SetupCodexNode(s => s
.WithLogLevel(CodexLogLevel.Trace, "marketplace", "sales", "proving", "reservations")
.WithStorageQuota(11.GB())
.EnableMarketplace(sellerInitialBalance));

Expand All @@ -59,11 +61,12 @@ public void MarketplaceExample()
var testFile = GenerateTestFile(fileSize);

var buyer = SetupCodexNode(s => s
.WithLogLevel(CodexLogLevel.Trace, "marketplace", "purchasing", "node", "restapi")
.WithBootstrapNode(seller)
.EnableMarketplace(buyerInitialBalance));

buyer.Marketplace.AssertThatBalance(Is.EqualTo(buyerInitialBalance));

var contentId = buyer.UploadFile(testFile);
var purchaseContract = buyer.Marketplace.RequestStorage(contentId,
pricePerSlotPerSecond: 2.TestTokens(),
Expand Down
87 changes: 87 additions & 0 deletions Tests/Marketplace/Interactions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using DistTestCore;
using DistTestCore.Codex;
using NUnit.Framework;
using Utils;

namespace Tests.BasicTests
{
[TestFixture]
public class SlotSelection : AutoBootstrapDistTest
{

[Test]
public void RequestExpiresIfNotFilledAndMoneyAreReturned()
{
var sellerInitialBalance = 234.TestTokens();
var buyerInitialBalance = 1000.TestTokens();
var fileSize = 10.MB();

var seller1 = SetupCodexNode(s => s
.WithLogLevel(CodexLogLevel.Trace, "marketplace", "sales", "proving", "reservations")
.WithStorageQuota(50.MB())
.EnableMarketplace(sellerInitialBalance)
.WithName("seller1"));

seller1.Marketplace.AssertThatBalance(Is.EqualTo(sellerInitialBalance));

// The two availabilities are needed until https://github.com/codex-storage/nim-codex/pull/535 is merged
seller1.Marketplace.MakeStorageAvailable(
size: 11.MB(),
minPricePerBytePerSecond: 1.TestTokens(),
maxCollateral: 20.TestTokens(),
maxDuration: TimeSpan.FromMinutes(3));
seller1.Marketplace.MakeStorageAvailable(
size: 11.MB(),
minPricePerBytePerSecond: 1.TestTokens(),
maxCollateral: 20.TestTokens(),
maxDuration: TimeSpan.FromMinutes(3));

var seller2 = SetupCodexNode(s => s
.WithLogLevel(CodexLogLevel.Trace, "marketplace", "sales", "proving", "reservations")
.WithStorageQuota(50.MB())
.EnableMarketplace(sellerInitialBalance)
.WithName("seller2"));

seller2.Marketplace.AssertThatBalance(Is.EqualTo(sellerInitialBalance));
seller2.Marketplace.MakeStorageAvailable(
size: 11.MB(),
minPricePerBytePerSecond: 1.TestTokens(),
maxCollateral: 20.TestTokens(),
maxDuration: TimeSpan.FromMinutes(3));
seller2.Marketplace.MakeStorageAvailable(
size: 11.MB(),
minPricePerBytePerSecond: 1.TestTokens(),
maxCollateral: 20.TestTokens(),
maxDuration: TimeSpan.FromMinutes(3));

var testFile = GenerateTestFile(fileSize);
var buyer = SetupCodexNode(s => s
.WithLogLevel(CodexLogLevel.Trace, "marketplace", "purchasing", "node", "restapi")
.EnableMarketplace(buyerInitialBalance)
.WithName("buyer"));

buyer.Marketplace.AssertThatBalance(Is.EqualTo(buyerInitialBalance));

var contentId = buyer.UploadFile(testFile);
var purchaseContract = buyer.Marketplace.RequestStorage(contentId,
pricePerSlotPerSecond: 2.TestTokens(),
requiredCollateral: 10.TestTokens(),
minRequiredNumberOfNodes: 3,
proofProbability: 5,
duration: TimeSpan.FromMinutes(1),
expiry: DateTime.Now.AddMinutes(2));

Time.Sleep(TimeSpan.FromSeconds(100));

seller1.Marketplace.AssertThatBalance(Is.LessThan(sellerInitialBalance), "Collateral was not placed.");
seller2.Marketplace.AssertThatBalance(Is.LessThan(sellerInitialBalance), "Collateral was not placed.");
buyer.Marketplace.AssertThatBalance(Is.LessThan(buyerInitialBalance), "Buyer was not charged for storage.");

purchaseContract.WaitForStorageContractFailed(TimeSpan.FromSeconds(120));

seller1.Marketplace.AssertThatBalance(Is.EqualTo(sellerInitialBalance), "Seller was not returned collateral.");
seller2.Marketplace.AssertThatBalance(Is.EqualTo(sellerInitialBalance), "Seller was not returned collateral.");
buyer.Marketplace.AssertThatBalance(Is.EqualTo(buyerInitialBalance), "Buyer was not returned money for the request.");
}
}
}
4 changes: 2 additions & 2 deletions Tests/Marketplace/Proofs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ public void HostThatMissesProofsIsPaidOutLessThanHostThatDoesNotMissProofs()

var seller = SetupCodexNode(s => s
.WithLogLevel(CodexLogLevel.Trace, "marketplace", "sales", "proving", "reservations")
.WithStorageQuota(20.GB())
.WithStorageQuota(21.GB())
.EnableMarketplace(sellerInitialBalance)
.WithName("seller"));

var sellerWithFailures = SetupCodexNode(s => s
.WithLogLevel(CodexLogLevel.Trace, "marketplace", "sales", "proving", "reservations")
.WithStorageQuota(20.GB())
.WithStorageQuota(21.GB())
.WithBootstrapNode(seller)
.WithSimulateProofFailures(2)
.EnableMarketplace(sellerInitialBalance)
Expand Down