diff --git a/Framework/Core/Http.cs b/Framework/Core/Http.cs index 61d13f8..0bd3117 100644 --- a/Framework/Core/Http.cs +++ b/Framework/Core/Http.cs @@ -51,7 +51,6 @@ namespace Core private string GetDescription() { - // todo: check this: return DebugStack.GetCallerName(skipFrames: 2); } diff --git a/ProjectPlugins/CodexPlugin/CodexContainerRecipe.cs b/ProjectPlugins/CodexPlugin/CodexContainerRecipe.cs index 84f2732..4e2cc8f 100644 --- a/ProjectPlugins/CodexPlugin/CodexContainerRecipe.cs +++ b/ProjectPlugins/CodexPlugin/CodexContainerRecipe.cs @@ -7,7 +7,7 @@ namespace CodexPlugin { public class CodexContainerRecipe : ContainerRecipeFactory { - private const string DefaultDockerImage = "codexstorage/nim-codex:sha-644c83b-dist-tests"; + private const string DefaultDockerImage = "codexstorage/nim-codex:sha-f2f1dd5-dist-tests"; public const string ApiPortTag = "codex_api_port"; public const string ListenPortTag = "codex_listen_port"; public const string MetricsPortTag = "codex_metrics_port"; diff --git a/Tests/CodexTests/BasicTests/ExampleTests.cs b/Tests/CodexTests/BasicTests/ExampleTests.cs index d308f8f..e7453b5 100644 --- a/Tests/CodexTests/BasicTests/ExampleTests.cs +++ b/Tests/CodexTests/BasicTests/ExampleTests.cs @@ -1,12 +1,9 @@ -using CodexContractsPlugin; -using CodexPlugin; +using CodexPlugin; using DistTestCore; using GethPlugin; using MetricsPlugin; -using Nethereum.Hex.HexConvertors.Extensions; using NUnit.Framework; using Utils; -using Request = CodexContractsPlugin.Marketplace.Request; namespace CodexTests.BasicTests { @@ -50,88 +47,6 @@ namespace CodexTests.BasicTests metrics[1].AssertThat("libp2p_peers", Is.EqualTo(1)); } - [Test] - public void MarketplaceExample() - { - var sellerInitialBalance = 234.TestTokens(); - var buyerInitialBalance = 100000.TestTokens(); - var fileSize = 10.MB(); - - var geth = Ci.StartGethNode(s => s.IsMiner().WithName("disttest-geth")); - var contracts = Ci.StartCodexContracts(geth); - - var seller = AddCodex(s => s - .WithName("Seller") - .WithLogLevel(CodexLogLevel.Trace, new CodexLogCustomTopics(CodexLogLevel.Error, CodexLogLevel.Error, CodexLogLevel.Warn) - { - ContractClock = CodexLogLevel.Trace, - }) - .WithStorageQuota(11.GB()) - .EnableMarketplace(geth, contracts, m => m - .WithInitial(10.Eth(), sellerInitialBalance) - .AsStorageNode() - .AsValidator())); - - AssertBalance(contracts, seller, Is.EqualTo(sellerInitialBalance)); - - var availability = new StorageAvailability( - totalSpace: 10.GB(), - maxDuration: TimeSpan.FromMinutes(30), - minPriceForTotalSpace: 1.TestTokens(), - maxCollateral: 20.TestTokens() - ); - seller.Marketplace.MakeStorageAvailable(availability); - - var testFile = GenerateTestFile(fileSize); - - var buyer = AddCodex(s => s - .WithName("Buyer") - .WithBootstrapNode(seller) - .EnableMarketplace(geth, contracts, m => m - .WithInitial(10.Eth(), buyerInitialBalance))); - - AssertBalance(contracts, buyer, Is.EqualTo(buyerInitialBalance)); - - var contentId = buyer.UploadFile(testFile); - - var purchase = new StoragePurchaseRequest(contentId) - { - PricePerSlotPerSecond = 2.TestTokens(), - RequiredCollateral = 10.TestTokens(), - MinRequiredNumberOfNodes = 5, - NodeFailureTolerance = 2, - ProofProbability = 5, - Duration = TimeSpan.FromMinutes(5), - Expiry = TimeSpan.FromMinutes(4) - }; - - var purchaseContract = buyer.Marketplace.RequestStorage(purchase); - - Time.Retry(() => - { - var slotFilledEvents = contracts.GetSlotFilledEvents(GetTestRunTimeRange()); - - Log($"SlotFilledEvents: {slotFilledEvents.Length} - NumSlots: {purchase.MinRequiredNumberOfNodes}"); - - if (slotFilledEvents.Length != purchase.MinRequiredNumberOfNodes) throw new Exception("not yet"); - }, Convert.ToInt32(purchase.Duration.TotalSeconds / 2) + 10, TimeSpan.FromSeconds(2), "Checking SlotFilled events"); - - purchaseContract.WaitForStorageContractStarted(); - - AssertBalance(contracts, seller, Is.LessThan(sellerInitialBalance), "Collateral was not placed."); - - var request = GetOnChainStorageRequest(contracts); - AssertStorageRequest(request, purchase, contracts, buyer); - AssertSlotFilledEvents(contracts, purchase, request, seller); - AssertContractSlot(contracts, request, 0, seller); - - purchaseContract.WaitForStorageContractFinished(); - - AssertBalance(contracts, seller, Is.GreaterThan(sellerInitialBalance), "Seller was not paid for storage."); - AssertBalance(contracts, buyer, Is.LessThan(buyerInitialBalance), "Buyer was not charged for storage."); - Assert.That(contracts.GetRequestState(request), Is.EqualTo(RequestState.Finished)); - } - [Test] public void GethBootstrapTest() { @@ -148,43 +63,5 @@ namespace CodexTests.BasicTests Assert.That(bootN, Is.EqualTo(followN)); Assert.That(discN, Is.LessThan(bootN)); } - - private void AssertSlotFilledEvents(ICodexContracts contracts, StoragePurchaseRequest purchase, Request request, ICodexNode seller) - { - // Expect 1 fulfilled event for the purchase. - var requestFulfilledEvents = contracts.GetRequestFulfilledEvents(GetTestRunTimeRange()); - Assert.That(requestFulfilledEvents.Length, Is.EqualTo(1)); - CollectionAssert.AreEqual(request.RequestId, requestFulfilledEvents[0].RequestId); - - // Expect 1 filled-slot event for each slot in the purchase. - var filledSlotEvents = contracts.GetSlotFilledEvents(GetTestRunTimeRange()); - Assert.That(filledSlotEvents.Length, Is.EqualTo(purchase.MinRequiredNumberOfNodes)); - for (var i = 0; i < purchase.MinRequiredNumberOfNodes; i++) - { - var filledSlotEvent = filledSlotEvents.Single(e => e.SlotIndex == i); - Assert.That(filledSlotEvent.RequestId.ToHex(), Is.EqualTo(request.RequestId.ToHex())); - Assert.That(filledSlotEvent.Host, Is.EqualTo(seller.EthAddress)); - } - } - - private void AssertStorageRequest(Request request, StoragePurchaseRequest purchase, ICodexContracts contracts, ICodexNode buyer) - { - Assert.That(contracts.GetRequestState(request), Is.EqualTo(RequestState.Started)); - Assert.That(request.ClientAddress, Is.EqualTo(buyer.EthAddress)); - Assert.That(request.Ask.Slots, Is.EqualTo(purchase.MinRequiredNumberOfNodes)); - } - - private Request GetOnChainStorageRequest(ICodexContracts contracts) - { - var requests = contracts.GetStorageRequests(GetTestRunTimeRange()); - Assert.That(requests.Length, Is.EqualTo(1)); - return requests.Single(); - } - - private void AssertContractSlot(ICodexContracts contracts, Request request, int contractSlotIndex, ICodexNode expectedSeller) - { - var slotHost = contracts.GetSlotHost(request, contractSlotIndex); - Assert.That(slotHost, Is.EqualTo(expectedSeller.EthAddress)); - } } } diff --git a/Tests/CodexTests/BasicTests/MarketplaceTests.cs b/Tests/CodexTests/BasicTests/MarketplaceTests.cs new file mode 100644 index 0000000..cb80008 --- /dev/null +++ b/Tests/CodexTests/BasicTests/MarketplaceTests.cs @@ -0,0 +1,138 @@ +using CodexContractsPlugin; +using CodexContractsPlugin.Marketplace; +using CodexPlugin; +using GethPlugin; +using Nethereum.Hex.HexConvertors.Extensions; +using NUnit.Framework; +using Utils; + +namespace CodexTests.BasicTests +{ + [TestFixture] + public class MarketplaceTests : AutoBootstrapDistTest + { + [Test] + public void MarketplaceExample() + { + var hostInitialBalance = 234.TestTokens(); + var clientInitialBalance = 100000.TestTokens(); + var fileSize = 10.MB(); + + var geth = Ci.StartGethNode(s => s.IsMiner().WithName("disttest-geth")); + var contracts = Ci.StartCodexContracts(geth); + + var host = AddCodex(s => s + .WithName("Host") + .WithLogLevel(CodexLogLevel.Trace, new CodexLogCustomTopics(CodexLogLevel.Error, CodexLogLevel.Error, CodexLogLevel.Warn) + { + ContractClock = CodexLogLevel.Trace, + }) + .WithStorageQuota(11.GB()) + .EnableMarketplace(geth, contracts, m => m + .WithInitial(10.Eth(), hostInitialBalance) + .AsStorageNode() + .AsValidator())); + + AssertBalance(contracts, host, Is.EqualTo(hostInitialBalance)); + + var availability = new StorageAvailability( + totalSpace: 10.GB(), + maxDuration: TimeSpan.FromMinutes(30), + minPriceForTotalSpace: 1.TestTokens(), + maxCollateral: 20.TestTokens() + ); + host.Marketplace.MakeStorageAvailable(availability); + + var testFile = GenerateTestFile(fileSize); + + var client = AddCodex(s => s + .WithName("Client") + .EnableMarketplace(geth, contracts, m => m + .WithInitial(10.Eth(), clientInitialBalance))); + + AssertBalance(contracts, client, Is.EqualTo(clientInitialBalance)); + + var contentId = client.UploadFile(testFile); + + var purchase = new StoragePurchaseRequest(contentId) + { + PricePerSlotPerSecond = 2.TestTokens(), + RequiredCollateral = 10.TestTokens(), + MinRequiredNumberOfNodes = 5, + NodeFailureTolerance = 2, + ProofProbability = 5, + Duration = TimeSpan.FromMinutes(5), + Expiry = TimeSpan.FromMinutes(4) + }; + + var purchaseContract = client.Marketplace.RequestStorage(purchase); + + WaitForAllSlotFilledEvents(contracts, purchase); + + purchaseContract.WaitForStorageContractStarted(); + + AssertBalance(contracts, host, Is.LessThan(hostInitialBalance), "Collateral was not placed."); + + var request = GetOnChainStorageRequest(contracts); + AssertStorageRequest(request, purchase, contracts, client); + AssertSlotFilledEvents(contracts, purchase, request, host); + AssertContractSlot(contracts, request, 0, host); + + purchaseContract.WaitForStorageContractFinished(); + + AssertBalance(contracts, host, Is.GreaterThan(hostInitialBalance), "Seller was not paid for storage."); + AssertBalance(contracts, client, Is.LessThan(clientInitialBalance), "Buyer was not charged for storage."); + Assert.That(contracts.GetRequestState(request), Is.EqualTo(RequestState.Finished)); + } + + private void WaitForAllSlotFilledEvents(ICodexContracts contracts, StoragePurchaseRequest purchase) + { + Time.Retry(() => + { + var slotFilledEvents = contracts.GetSlotFilledEvents(GetTestRunTimeRange()); + + Log($"SlotFilledEvents: {slotFilledEvents.Length} - NumSlots: {purchase.MinRequiredNumberOfNodes}"); + + if (slotFilledEvents.Length != purchase.MinRequiredNumberOfNodes) throw new Exception(); + }, Convert.ToInt32(purchase.Duration.TotalSeconds / 5) + 10, TimeSpan.FromSeconds(5), "Checking SlotFilled events"); + } + + private void AssertSlotFilledEvents(ICodexContracts contracts, StoragePurchaseRequest purchase, Request request, ICodexNode seller) + { + // Expect 1 fulfilled event for the purchase. + var requestFulfilledEvents = contracts.GetRequestFulfilledEvents(GetTestRunTimeRange()); + Assert.That(requestFulfilledEvents.Length, Is.EqualTo(1)); + CollectionAssert.AreEqual(request.RequestId, requestFulfilledEvents[0].RequestId); + + // Expect 1 filled-slot event for each slot in the purchase. + var filledSlotEvents = contracts.GetSlotFilledEvents(GetTestRunTimeRange()); + Assert.That(filledSlotEvents.Length, Is.EqualTo(purchase.MinRequiredNumberOfNodes)); + for (var i = 0; i < purchase.MinRequiredNumberOfNodes; i++) + { + var filledSlotEvent = filledSlotEvents.Single(e => e.SlotIndex == i); + Assert.That(filledSlotEvent.RequestId.ToHex(), Is.EqualTo(request.RequestId.ToHex())); + Assert.That(filledSlotEvent.Host, Is.EqualTo(seller.EthAddress)); + } + } + + private void AssertStorageRequest(Request request, StoragePurchaseRequest purchase, ICodexContracts contracts, ICodexNode buyer) + { + Assert.That(contracts.GetRequestState(request), Is.EqualTo(RequestState.Started)); + Assert.That(request.ClientAddress, Is.EqualTo(buyer.EthAddress)); + Assert.That(request.Ask.Slots, Is.EqualTo(purchase.MinRequiredNumberOfNodes)); + } + + private Request GetOnChainStorageRequest(ICodexContracts contracts) + { + var requests = contracts.GetStorageRequests(GetTestRunTimeRange()); + Assert.That(requests.Length, Is.EqualTo(1)); + return requests.Single(); + } + + private void AssertContractSlot(ICodexContracts contracts, Request request, int contractSlotIndex, ICodexNode expectedSeller) + { + var slotHost = contracts.GetSlotHost(request, contractSlotIndex); + Assert.That(slotHost, Is.EqualTo(expectedSeller.EthAddress)); + } + } +}