Compare commits

...
Author SHA1 Message Date
Adam Uhlíř 8ca9feb9e2 test: testing slot expiry 2023-09-07 15:13:37 +02:00
Eric bd9edd3931 bump storage quota 2023-08-18 16:02:21 +10:00
Eric 82ce11e848 bump codex contracts eth image to latest 2023-08-18 15:39:04 +10:00
Eric 2155634414 improve simulated proof failure test
The test is mostly working, except that the node needs to be updated to call the onReservationAdded callback once availability marked unused
2023-08-18 10:22:51 +10:00
Eric 0e342adcdf change geth/prometheus images to latest 2023-08-18 09:52:21 +10:00
Eric 255dc127c8 Simulated proof failure test now working 2023-08-16 17:21:54 +10:00
Eric 83d907152a bump codex image to fixes with json parsing and empty block retreival 2023-08-16 14:04:23 +10:00
Eric c833a29de5 bump codex contracts image to latest with fixes for node/hardhat version 2023-08-16 14:04:23 +10:00
Eric abcb4725cd WIP Get simulate proof failures test running
- Add WithLogLevel to specify log level in container logs
- Update nim-codex image (should be latest, check with Slava)
- Fix hardhat deployment path
- Remove Arm64 preprocessor directive now that we have multiarch image
2023-08-16 14:04:22 +10:00
Eric 059c15d9de docker image updates 2023-08-16 14:04:22 +10:00
Eric a1d9756403 update WithLogLevel to accept params string[] 2023-08-16 14:02:56 +10:00
Eric Mastro 8ffa1fde4a Add validator and simulation of proof failures
- add validator to codex node setup, and start companion node for it
- add simulate-proof-failures to codex node setup
- allow log-level to log topics at a specific level

# Conflicts:
#	DistTestCore/Codex/CodexContainerRecipe.cs
#	DistTestCore/Codex/CodexStartupConfig.cs
#	DistTestCore/CodexSetup.cs
2023-08-16 14:02:55 +10:00
15 changed files with 335 additions and 30 deletions
+18 -4
View File
@@ -5,8 +5,7 @@ namespace DistTestCore.Codex
{
public class CodexContainerRecipe : ContainerRecipeFactory
{
private const string DefaultDockerImage = "codexstorage/nim-codex:sha-7efa917";
public const string DefaultDockerImage = "codexstorage/nim-codex:latest-dist-tests";
public const string MetricsPortTag = "metrics_port";
public const string DiscoveryPortTag = "discovery-port";
@@ -31,7 +30,7 @@ namespace DistTestCore.Codex
AddEnvVar("CODEX_DATA_DIR", $"datadir{ContainerNumber}");
AddInternalPortAndVar("CODEX_DISC_PORT", DiscoveryPortTag);
AddEnvVar("CODEX_LOG_LEVEL", config.LogLevel.ToString()!.ToUpperInvariant());
AddEnvVar("CODEX_LOG_LEVEL", config.LogLevelWithTopics());
// This makes the node announce itself to its local (pod) IP address.
AddEnvVar("NAT_IP_AUTO", "true");
@@ -58,6 +57,14 @@ namespace DistTestCore.Codex
AddInternalPortAndVar("CODEX_METRICS_PORT", tag: MetricsPortTag);
}
if (config.SimulateProofFailures != null)
{
AddEnvVar("CODEX_SIMULATE_PROOF_FAILURES", config.SimulateProofFailures.ToString()!);
}
// if (config.EnableValidator == true)
// {
// AddEnvVar("CODEX_VALIDATOR", "true");
// }
if (config.MarketplaceConfig != null)
{
var gethConfig = startupConfig.Get<GethStartResult>();
@@ -75,8 +82,15 @@ namespace DistTestCore.Codex
if (config.MarketplaceConfig.IsValidator)
{
AddEnvVar("CODEX_VALIDATOR", "true");
AddEnvVar("CODEX_VALIDATOR", "true");
}
}
// if (config.MarketplaceConfig != null) {
// AddEnvVar("CODEX_PERSISTENCE", "true");
// }
if(!string.IsNullOrEmpty(config.NameOverride)) {
AddEnvVar("CODEX_NODENAME", config.NameOverride);
}
}
+14 -1
View File
@@ -10,13 +10,26 @@ namespace DistTestCore.Codex
LogLevel = logLevel;
}
public string LogLevelWithTopics()
{
var level = LogLevel.ToString()!.ToUpperInvariant();
if (LogTopics != null && LogTopics.Count() > 0)
{
level = $"INFO;{level}: {string.Join(",", LogTopics.Where(s => !string.IsNullOrEmpty(s)))}";
}
return level;
}
public string? NameOverride { get; set; }
public Location Location { get; set; }
public CodexLogLevel LogLevel { get; }
public CodexLogLevel LogLevel { get; set; }
public string[]? LogTopics { get; set; }
public ByteSize? StorageQuota { get; set; }
public bool MetricsEnabled { get; set; }
public MarketplaceInitialConfig? MarketplaceConfig { get; set; }
public string? BootstrapSpr { get; set; }
public int? BlockTTL { get; set; }
public uint? SimulateProofFailures { get; set; }
public bool? EnableValidator { get; set; }
}
}
+45 -4
View File
@@ -8,6 +8,12 @@ namespace DistTestCore
{
ICodexSetup WithName(string name);
ICodexSetup At(Location location);
ICodexSetup WithLogLevel(CodexLogLevel level);
/// <summary>
/// Sets the log level for codex. The default level is INFO and the
/// log level is applied only to the supplied topics.
/// </summary>
ICodexSetup WithLogLevel(CodexLogLevel level, params string[] topics);
ICodexSetup WithBootstrapNode(IOnlineCodexNode node);
ICodexSetup WithStorageQuota(ByteSize storageQuota);
ICodexSetup WithBlockTTL(TimeSpan duration);
@@ -15,8 +21,16 @@ namespace DistTestCore
ICodexSetup EnableMarketplace(TestToken initialBalance);
ICodexSetup EnableMarketplace(TestToken initialBalance, Ether initialEther);
ICodexSetup EnableMarketplace(TestToken initialBalance, Ether initialEther, bool isValidator);
/// <summary>
/// Provides an invalid proof every N proofs
/// </summary>
ICodexSetup WithSimulateProofFailures(uint failEveryNProofs);
/// <summary>
/// Enables the validation module in the node
/// </summary>
// ICodexSetup WithValidator();
}
public class CodexSetup : CodexStartupConfig, ICodexSetup
{
public int NumberOfNodes { get; }
@@ -45,6 +59,19 @@ namespace DistTestCore
return this;
}
public ICodexSetup WithLogLevel(CodexLogLevel level)
{
LogLevel = level;
return this;
}
public ICodexSetup WithLogLevel(CodexLogLevel level, params string[] topics)
{
LogLevel = level;
LogTopics = topics;
return this;
}
public ICodexSetup WithStorageQuota(ByteSize storageQuota)
{
StorageQuota = storageQuota;
@@ -70,7 +97,7 @@ namespace DistTestCore
public ICodexSetup EnableMarketplace(TestToken initialBalance, Ether initialEther)
{
return EnableMarketplace(initialBalance, initialEther, false);
return EnableMarketplace(initialBalance, initialEther, false);
}
public ICodexSetup EnableMarketplace(TestToken initialBalance, Ether initialEther, bool isValidator)
@@ -79,6 +106,18 @@ namespace DistTestCore
return this;
}
public ICodexSetup WithSimulateProofFailures(uint failEveryNProofs)
{
SimulateProofFailures = failEveryNProofs;
return this;
}
// public ICodexSetup WithValidator()
// {
// EnableValidator = true;
// return this;
// }
public string Describe()
{
var args = string.Join(',', DescribeArgs());
@@ -87,9 +126,11 @@ namespace DistTestCore
private IEnumerable<string> DescribeArgs()
{
yield return $"LogLevel={LogLevel}";
yield return $"LogLevel={LogLevelWithTopics()}";
if (BootstrapSpr != null) yield return $"BootstrapNode={BootstrapSpr}";
if (StorageQuota != null) yield return $"StorageQuote={StorageQuota}";
if (StorageQuota != null) yield return $"StorageQuota={StorageQuota}";
if (SimulateProofFailures != null) yield return $"SimulateProofFailures={SimulateProofFailures}";
if (MarketplaceConfig != null) yield return $"IsValidator={MarketplaceConfig.IsValidator}";
}
}
}
+1
View File
@@ -20,6 +20,7 @@ namespace DistTestCore
LogSeparator();
LogStart($"Starting {codexSetup.Describe()}...");
var gethStartResult = lifecycle.GethStarter.BringOnlineMarketplaceFor(codexSetup);
gethStartResult = lifecycle.GethStarter.BringOnlineValidatorFor(codexSetup, gethStartResult);
var startupConfig = CreateStartupConfig(gethStartResult, codexSetup);
-4
View File
@@ -5,10 +5,6 @@
<RootNamespace>DistTestCore</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsArm64 Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)' == 'Arm64'">true</IsArm64>
</PropertyGroup>
<PropertyGroup Condition="'$(IsArm64)'=='true'">
<DefineConstants>Arm64</DefineConstants>
</PropertyGroup>
<ItemGroup>
+11
View File
@@ -30,6 +30,17 @@ namespace DistTestCore
return CreateGethStartResult(marketplaceNetwork, companionNode);
}
public GethStartResult BringOnlineValidatorFor(CodexSetup codexSetup, GethStartResult previousResult)
{
// allow marketplace and validator to be enabled on the same Codex node
if (previousResult.CompanionNode != null || (codexSetup.EnableValidator ?? false) == false) return previousResult;
var marketplaceNetwork = marketplaceNetworkCache.Get();
var companionNode = StartCompanionNode(codexSetup, marketplaceNetwork);
return CreateGethStartResult(marketplaceNetwork, companionNode);
}
private void TransferInitialBalance(MarketplaceNetwork marketplaceNetwork, MarketplaceInitialConfig marketplaceConfig, GethCompanionNodeInfo companionNode)
{
if (marketplaceConfig.InitialTestTokens.Amount == 0) return;
+1 -2
View File
@@ -10,8 +10,7 @@ namespace DistTestCore.Helpers
{
try
{
var c = constraint.Resolve();
Time.WaitUntil(() => c.ApplyTo(actual()).IsSuccess);
Time.WaitUntil(() => constraint.Resolve().ApplyTo(actual()).IsSuccess);
}
catch (TimeoutException)
{
@@ -8,7 +8,7 @@ namespace DistTestCore.Marketplace
public const string MarketplaceArtifactFilename = "/hardhat/artifacts/contracts/Marketplace.sol/Marketplace.json";
public override string AppName => "codex-contracts";
public override string Image => "codexstorage/dist-tests-codex-contracts-eth:sha-d6fbfdc";
public override string Image => "codexstorage/codex-contracts-eth:latest-dist-tests";
protected override void Initialize(StartupConfig startupConfig)
{
@@ -11,7 +11,7 @@ namespace DistTestCore.Marketplace
public const string AccountsFilename = "accounts.csv";
public override string AppName => "geth";
public override string Image => "codexstorage/dist-tests-geth:sha-b788a2d";
public override string Image => "codexstorage/dist-tests-geth:latest";
protected override void Initialize(StartupConfig startupConfig)
{
+44 -11
View File
@@ -5,6 +5,7 @@ using Newtonsoft.Json;
using NUnit.Framework;
using NUnit.Framework.Constraints;
using System.Numerics;
using System.Linq;
using Utils;
namespace DistTestCore.Marketplace
@@ -12,7 +13,7 @@ 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();
}
@@ -32,17 +33,18 @@ namespace DistTestCore.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}... (" +
@@ -50,7 +52,9 @@ namespace DistTestCore.Marketplace
$"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);
@@ -123,7 +127,7 @@ namespace DistTestCore.Marketplace
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!;
@@ -187,6 +191,29 @@ namespace DistTestCore.Marketplace
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>
@@ -205,12 +232,18 @@ namespace DistTestCore.Marketplace
}
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);
@@ -229,10 +262,10 @@ namespace DistTestCore.Marketplace
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)
@@ -5,7 +5,7 @@ namespace DistTestCore.Metrics
public class PrometheusContainerRecipe : ContainerRecipeFactory
{
public override string AppName => "prometheus";
public override string Image => "codexstorage/dist-tests-prometheus:sha-f97d7fd";
public override string Image => "codexstorage/dist-tests-prometheus:latest";
protected override void Initialize(StartupConfig startupConfig)
{
+10
View File
@@ -58,6 +58,16 @@
{
return $"{Amount} TestTokens";
}
public static TestToken operator - (TestToken t1, int amount)
{
return t1 - Convert.ToDecimal(amount);
}
public static TestToken operator - (TestToken t1, decimal amount)
{
return (t1.Amount - amount).TestTokens();
}
}
public static class TokensIntExtensions
+4 -1
View File
@@ -1,4 +1,5 @@
using DistTestCore;
using DistTestCore.Codex;
using NUnit.Framework;
namespace Tests.BasicTests
@@ -46,6 +47,7 @@ namespace Tests.BasicTests
var fileSize = 10.MB();
var seller = SetupCodexNode(s => s
.WithLogLevel(CodexLogLevel.Trace, "marketplace", "sales", "proving", "reservations")
.WithStorageQuota(11.GB())
.EnableMarketplace(sellerInitialBalance));
@@ -59,11 +61,12 @@ namespace Tests.BasicTests
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(),
+87
View File
@@ -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.");
}
}
}
+97
View File
@@ -0,0 +1,97 @@
using DistTestCore;
using DistTestCore.Codex;
using NUnit.Framework;
using Utils;
namespace Tests.BasicTests
{
[TestFixture]
public class MarketplaceTests : DistTest
{
[Test]
public void HostThatMissesProofsIsPaidOutLessThanHostThatDoesNotMissProofs()
{
var sellerInitialBalance = 234.TestTokens();
var buyerInitialBalance = 1000.TestTokens();
var seller = SetupCodexNode(s => s
.WithLogLevel(CodexLogLevel.Trace, "marketplace", "sales", "proving", "reservations")
.WithStorageQuota(21.GB())
.EnableMarketplace(sellerInitialBalance)
.WithName("seller"));
var sellerWithFailures = SetupCodexNode(s => s
.WithLogLevel(CodexLogLevel.Trace, "marketplace", "sales", "proving", "reservations")
.WithStorageQuota(21.GB())
.WithBootstrapNode(seller)
.WithSimulateProofFailures(2)
.EnableMarketplace(sellerInitialBalance)
.WithName("seller with failures"));
var buyer = SetupCodexNode(s => s
.WithLogLevel(CodexLogLevel.Trace, "marketplace", "purchasing", "node", "restapi")
.WithBootstrapNode(seller)
.EnableMarketplace(buyerInitialBalance)
.WithName("buyer"));
var validator = SetupCodexNode(s => s
.WithLogLevel(CodexLogLevel.Trace, "validator")
.WithBootstrapNode(seller)
// .WithValidator()
.EnableMarketplace(0.TestTokens(), 2.Eth(), true)
.WithName("validator"));
seller.Marketplace.AssertThatBalance(Is.EqualTo(sellerInitialBalance));
sellerWithFailures.Marketplace.AssertThatBalance(Is.EqualTo(sellerInitialBalance));
buyer.Marketplace.AssertThatBalance(Is.EqualTo(buyerInitialBalance));
seller.Marketplace.MakeStorageAvailable(
size: 10.GB(),
minPricePerBytePerSecond: 1.TestTokens(),
maxCollateral: 20.TestTokens(),
maxDuration: TimeSpan.FromMinutes(3));
sellerWithFailures.Marketplace.MakeStorageAvailable(
size: 10.GB(),
minPricePerBytePerSecond: 1.TestTokens(),
maxCollateral: 20.TestTokens(),
maxDuration: TimeSpan.FromMinutes(3));
seller.Marketplace.MakeStorageAvailable(
size: 10.GB(),
minPricePerBytePerSecond: 1.TestTokens(),
maxCollateral: 20.TestTokens(),
maxDuration: TimeSpan.FromMinutes(3));
sellerWithFailures.Marketplace.MakeStorageAvailable(
size: 10.GB(),
minPricePerBytePerSecond: 1.TestTokens(),
maxCollateral: 20.TestTokens(),
maxDuration: TimeSpan.FromMinutes(3));
var fileSize = 10.MB();
var testFile = GenerateTestFile(fileSize);
var contentId = buyer.UploadFile(testFile);
var purchaseContract = buyer.Marketplace.RequestStorage(
contentId,
pricePerSlotPerSecond: 2.TestTokens(),
requiredCollateral: 10.TestTokens(),
minRequiredNumberOfNodes: 2,
proofProbability: 2,
duration: TimeSpan.FromMinutes(3));
// Time.Sleep(TimeSpan.FromMinutes(1));
// seller.Marketplace.AssertThatBalance(Is.LessThan(sellerInitialBalance), "Collateral was not placed.");
purchaseContract.WaitForStorageContractStarted(fileSize);
purchaseContract.WaitForStorageContractFinished();
var sellerBalance = seller.Marketplace.GetBalance();
sellerWithFailures.Marketplace.AssertThatBalance(Is.LessThan(sellerBalance), "Seller that was slashed should have less balance than seller that was not slashed.");
new List<IOnlineCodexNode>(){seller, sellerWithFailures, buyer, validator}.ForEach(node => node.DownloadLog());
}
}
}