From a6c2bf5230a634fc733437e53a4e211e7eff64a3 Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 20 Mar 2024 11:11:41 +0100 Subject: [PATCH] Cleanup marketplace-access methods --- .../CodexPlugin/MarketplaceAccess.cs | 90 ++++------------ .../CodexPlugin/MarketplaceTypes.cs | 101 ++++++++++++++++++ .../BasicTests/ContinuousSubstitute.cs | 13 ++- Tests/CodexTests/BasicTests/ExampleTests.cs | 39 ++++--- Tools/CodexNetDeployer/CodexNodeStarter.cs | 11 +- 5 files changed, 158 insertions(+), 96 deletions(-) create mode 100644 ProjectPlugins/CodexPlugin/MarketplaceTypes.cs diff --git a/ProjectPlugins/CodexPlugin/MarketplaceAccess.cs b/ProjectPlugins/CodexPlugin/MarketplaceAccess.cs index 6077cee5..f999bffe 100644 --- a/ProjectPlugins/CodexPlugin/MarketplaceAccess.cs +++ b/ProjectPlugins/CodexPlugin/MarketplaceAccess.cs @@ -2,15 +2,13 @@ using Logging; using Newtonsoft.Json; using Utils; -using System.Numerics; namespace CodexPlugin { public interface IMarketplaceAccess { - string MakeStorageAvailable(ByteSize size, TestToken minPriceForTotalSpace, 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, TimeSpan expiry); + string MakeStorageAvailable(StorageAvailability availability); + StoragePurchaseContract RequestStorage(StoragePurchase purchase); } public class MarketplaceAccess : IMarketplaceAccess @@ -24,35 +22,12 @@ namespace CodexPlugin this.codexAccess = codexAccess; } - public StoragePurchaseContract RequestStorage(ContentId contentId, TestToken pricePerSlotPerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration) + public StoragePurchaseContract RequestStorage(StoragePurchase purchase) { - return RequestStorage(contentId, pricePerSlotPerSecond, requiredCollateral, minRequiredNumberOfNodes, proofProbability, duration, duration / 2); - } + purchase.Log(log); + var request = purchase.ToApiRequest(); - public StoragePurchaseContract RequestStorage(ContentId contentId, TestToken pricePerSlotPerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration, TimeSpan expiry) - { - var expireUtc = DateTimeOffset.UtcNow.ToUnixTimeSeconds() + expiry.TotalSeconds; - - var request = new CodexSalesRequestStorageRequest - { - duration = ToDecInt(duration.TotalSeconds), - proofProbability = ToDecInt(proofProbability), - reward = ToDecInt(pricePerSlotPerSecond), - collateral = ToDecInt(requiredCollateral), - expiry = ToDecInt(expireUtc), - nodes = minRequiredNumberOfNodes, - tolerance = null, - }; - - Log($"Requesting storage for: {contentId.Id}... (" + - $"pricePerSlotPerSecond: {pricePerSlotPerSecond}, " + - $"requiredCollateral: {requiredCollateral}, " + - $"minRequiredNumberOfNodes: {minRequiredNumberOfNodes}, " + - $"proofProbability: {proofProbability}, " + - $"expiry: {Time.FormatDuration(expiry)}, " + - $"duration: {Time.FormatDuration(duration)})"); - - var response = codexAccess.RequestStorage(request, contentId.Id); + var response = codexAccess.RequestStorage(request, purchase.ContentId.Id); if (response == "Purchasing not available" || response == "Expiry required" || @@ -64,24 +39,13 @@ namespace CodexPlugin Log($"Storage requested successfully. PurchaseId: '{response}'."); - return new StoragePurchaseContract(log, codexAccess, response, duration); + return new StoragePurchaseContract(log, codexAccess, response, purchase); } - public string MakeStorageAvailable(ByteSize totalSpace, TestToken minPriceForTotalSpace, TestToken maxCollateral, TimeSpan maxDuration) + public string MakeStorageAvailable(StorageAvailability availability) { - var request = new CodexSalesAvailabilityRequest - { - size = ToDecInt(totalSpace.SizeInBytes), - duration = ToDecInt(maxDuration.TotalSeconds), - maxCollateral = ToDecInt(maxCollateral), - minPrice = ToDecInt(minPriceForTotalSpace) - }; - - Log($"Making storage available... (" + - $"size: {totalSpace}, " + - $"minPriceForTotalSpace: {minPriceForTotalSpace}, " + - $"maxCollateral: {maxCollateral}, " + - $"maxDuration: {Time.FormatDuration(maxDuration)})"); + availability.Log(log); + var request = availability.ToApiRequest(); var response = codexAccess.SalesAvailability(request); @@ -90,18 +54,6 @@ namespace CodexPlugin return response.id; } - private string ToDecInt(double d) - { - var i = new BigInteger(d); - return i.ToString("D"); - } - - public string ToDecInt(TestToken t) - { - var i = new BigInteger(t.Amount); - return i.ToString("D"); - } - private void Log(string msg) { log.Log($"{codexAccess.Container.Name} {msg}"); @@ -110,22 +62,16 @@ namespace CodexPlugin public class MarketplaceUnavailable : IMarketplaceAccess { - public StoragePurchaseContract RequestStorage(ContentId contentId, TestToken pricePerBytePerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration) - { - Unavailable(); - return null!; - } - - public StoragePurchaseContract RequestStorage(ContentId contentId, TestToken pricePerSlotPerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration, TimeSpan expiry) + public string MakeStorageAvailable(StorageAvailability availability) { Unavailable(); throw new NotImplementedException(); } - public string MakeStorageAvailable(ByteSize size, TestToken minPricePerBytePerSecond, TestToken maxCollateral, TimeSpan duration) + public StoragePurchaseContract RequestStorage(StoragePurchase purchase) { Unavailable(); - return string.Empty; + throw new NotImplementedException(); } private void Unavailable() @@ -141,16 +87,16 @@ namespace CodexPlugin private readonly CodexAccess codexAccess; private DateTime? contractStartUtc; - public StoragePurchaseContract(ILog log, CodexAccess codexAccess, string purchaseId, TimeSpan contractDuration) + public StoragePurchaseContract(ILog log, CodexAccess codexAccess, string purchaseId, StoragePurchase purchase) { this.log = log; this.codexAccess = codexAccess; PurchaseId = purchaseId; - ContractDuration = contractDuration; + Purchase = purchase; } public string PurchaseId { get; } - public TimeSpan ContractDuration { get; } + public StoragePurchase Purchase { get; } public void WaitForStorageContractStarted() { @@ -165,7 +111,7 @@ namespace CodexPlugin } var gracePeriod = TimeSpan.FromSeconds(10); var currentContractTime = DateTime.UtcNow - contractStartUtc!.Value; - var timeout = (ContractDuration - currentContractTime) + gracePeriod; + var timeout = (Purchase.Duration - currentContractTime) + gracePeriod; WaitForStorageContractState(timeout, "finished"); } @@ -177,7 +123,7 @@ namespace CodexPlugin } var gracePeriod = TimeSpan.FromSeconds(10); var currentContractTime = DateTime.UtcNow - contractStartUtc!.Value; - var timeout = (ContractDuration - currentContractTime) + gracePeriod; + var timeout = (Purchase.Duration - currentContractTime) + gracePeriod; WaitForStorageContractState(timeout, "finished"); } diff --git a/ProjectPlugins/CodexPlugin/MarketplaceTypes.cs b/ProjectPlugins/CodexPlugin/MarketplaceTypes.cs new file mode 100644 index 00000000..5987a4ee --- /dev/null +++ b/ProjectPlugins/CodexPlugin/MarketplaceTypes.cs @@ -0,0 +1,101 @@ +using CodexContractsPlugin; +using Logging; +using System.Numerics; +using Utils; + +namespace CodexPlugin +{ + public class StoragePurchase : MarketplaceType + { + public StoragePurchase(ContentId cid) + { + ContentId = cid; + } + + public ContentId ContentId { get; set; } + public TestToken PricePerSlotPerSecond { get; set; } = 1.TestTokens(); + public TestToken RequiredCollateral { get; set; } = 1.TestTokens(); + public uint MinRequiredNumberOfNodes { get; set; } + public uint NodeFailureTolerance { get; set; } + public int ProofProbability { get; set; } + public TimeSpan Duration { get; set; } + public TimeSpan Expiry { get; set; } + + public CodexSalesRequestStorageRequest ToApiRequest() + { + return new CodexSalesRequestStorageRequest + { + duration = ToDecInt(Duration.TotalSeconds), + proofProbability = ToDecInt(ProofProbability), + reward = ToDecInt(PricePerSlotPerSecond), + collateral = ToDecInt(RequiredCollateral), + expiry = ToDecInt(DateTimeOffset.UtcNow.ToUnixTimeSeconds() + Expiry.TotalSeconds), + nodes = MinRequiredNumberOfNodes, + tolerance = NodeFailureTolerance + }; + } + + public void Log(ILog log) + { + log.Log($"Requesting storage for: {ContentId.Id}... (" + + $"pricePerSlotPerSecond: {PricePerSlotPerSecond}, " + + $"requiredCollateral: {RequiredCollateral}, " + + $"minRequiredNumberOfNodes: {MinRequiredNumberOfNodes}, " + + $"nodeFailureTolerance: {NodeFailureTolerance}, " + + $"proofProbability: {ProofProbability}, " + + $"expiry: {Time.FormatDuration(Expiry)}, " + + $"duration: {Time.FormatDuration(Duration)})"); + } + } + + public class StorageAvailability : MarketplaceType + { + public StorageAvailability(ByteSize totalSpace, TimeSpan maxDuration, TestToken minPriceForTotalSpace, TestToken maxCollateral) + { + TotalSpace = totalSpace; + MaxDuration = maxDuration; + MinPriceForTotalSpace = minPriceForTotalSpace; + MaxCollateral = maxCollateral; + } + + public ByteSize TotalSpace { get; } + public TimeSpan MaxDuration { get; } + public TestToken MinPriceForTotalSpace { get; } + public TestToken MaxCollateral { get; } + + public CodexSalesAvailabilityRequest ToApiRequest() + { + return new CodexSalesAvailabilityRequest + { + size = ToDecInt(TotalSpace.SizeInBytes), + duration = ToDecInt(MaxDuration.TotalSeconds), + maxCollateral = ToDecInt(MaxCollateral), + minPrice = ToDecInt(MinPriceForTotalSpace) + }; + } + + public void Log(ILog log) + { + log.Log($"Making storage available... (" + + $"size: {TotalSpace}, " + + $"maxDuration: {Time.FormatDuration(MaxDuration)})" + + $"minPriceForTotalSpace: {MinPriceForTotalSpace}, " + + $"maxCollateral: {MaxCollateral}, "); + } + } + + public abstract class MarketplaceType + { + protected string ToDecInt(double d) + { + var i = new BigInteger(d); + return i.ToString("D"); + } + + protected string ToDecInt(TestToken t) + { + var i = new BigInteger(t.Amount); + return i.ToString("D"); + } + } +} diff --git a/Tests/CodexTests/BasicTests/ContinuousSubstitute.cs b/Tests/CodexTests/BasicTests/ContinuousSubstitute.cs index ad22bac3..5f4c3002 100644 --- a/Tests/CodexTests/BasicTests/ContinuousSubstitute.cs +++ b/Tests/CodexTests/BasicTests/ContinuousSubstitute.cs @@ -33,13 +33,16 @@ namespace CodexTests.BasicTests var rc = Ci.DeployMetricsCollector(nodes); + var availability = new StorageAvailability( + totalSpace: 500.MB(), + maxDuration: TimeSpan.FromMinutes(5), + minPriceForTotalSpace: 500.TestTokens(), + maxCollateral: 1024.TestTokens() + ); + foreach (var node in nodes) { - node.Marketplace.MakeStorageAvailable( - size: 500.MB(), - minPriceForTotalSpace: 500.TestTokens(), - maxCollateral: 1024.TestTokens(), - maxDuration: TimeSpan.FromMinutes(5)); + node.Marketplace.MakeStorageAvailable(availability); } var endTime = DateTime.UtcNow + TimeSpan.FromHours(10); diff --git a/Tests/CodexTests/BasicTests/ExampleTests.cs b/Tests/CodexTests/BasicTests/ExampleTests.cs index 8db821af..6b46917d 100644 --- a/Tests/CodexTests/BasicTests/ExampleTests.cs +++ b/Tests/CodexTests/BasicTests/ExampleTests.cs @@ -54,7 +54,7 @@ namespace CodexTests.BasicTests public void MarketplaceExample() { var sellerInitialBalance = 234.TestTokens(); - var buyerInitialBalance = 1000.TestTokens(); + var buyerInitialBalance = 100000.TestTokens(); var fileSize = 10.MB(); var geth = Ci.StartGethNode(s => s.IsMiner().WithName("disttest-geth")); @@ -69,11 +69,14 @@ namespace CodexTests.BasicTests .AsValidator())); AssertBalance(contracts, seller, Is.EqualTo(sellerInitialBalance)); - seller.Marketplace.MakeStorageAvailable( - size: 10.GB(), + + var availability = new StorageAvailability( + totalSpace: 10.GB(), + maxDuration: TimeSpan.FromMinutes(30), minPriceForTotalSpace: 1.TestTokens(), - maxCollateral: 20.TestTokens(), - maxDuration: TimeSpan.FromMinutes(30)); + maxCollateral: 20.TestTokens() + ); + seller.Marketplace.MakeStorageAvailable(availability); var testFile = GenerateTestFile(fileSize); @@ -85,20 +88,26 @@ namespace CodexTests.BasicTests AssertBalance(contracts, buyer, Is.EqualTo(buyerInitialBalance)); var contentId = buyer.UploadFile(testFile); - var purchaseContract = buyer.Marketplace.RequestStorage(contentId, - pricePerSlotPerSecond: 2.TestTokens(), - requiredCollateral: 10.TestTokens(), - minRequiredNumberOfNodes: 1, - proofProbability: 5, - duration: TimeSpan.FromMinutes(5), - expiry: TimeSpan.FromMinutes(4)); + + var purchase = new StoragePurchase(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); purchaseContract.WaitForStorageContractStarted(TimeSpan.FromMinutes(4)); AssertBalance(contracts, seller, Is.LessThan(sellerInitialBalance), "Collateral was not placed."); var request = GetOnChainStorageRequest(contracts); - AssertStorageRequest(request, contracts, buyer); + AssertStorageRequest(request, purchase, contracts, buyer); AssertSlotFilledEvents(contracts, request, seller); AssertContractSlot(contracts, request, 0, seller); @@ -141,11 +150,11 @@ namespace CodexTests.BasicTests Assert.That(filledSlotEvent.Host, Is.EqualTo(seller.EthAddress)); } - private void AssertStorageRequest(Request request, ICodexContracts contracts, ICodexNode buyer) + private void AssertStorageRequest(Request request, StoragePurchase 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(1)); + Assert.That(request.Ask.Slots, Is.EqualTo(purchase.MinRequiredNumberOfNodes)); } private Request GetOnChainStorageRequest(ICodexContracts contracts) diff --git a/Tools/CodexNetDeployer/CodexNodeStarter.cs b/Tools/CodexNetDeployer/CodexNodeStarter.cs index 71e7a6a6..663659b4 100644 --- a/Tools/CodexNetDeployer/CodexNodeStarter.cs +++ b/Tools/CodexNetDeployer/CodexNodeStarter.cs @@ -67,11 +67,14 @@ namespace CodexNetDeployer if (config.ShouldMakeStorageAvailable) { - var response = codexNode.Marketplace.MakeStorageAvailable( - size: config.StorageSell!.Value.MB(), + var availability = new StorageAvailability( + totalSpace: config.StorageSell!.Value.MB(), + maxDuration: TimeSpan.FromSeconds(config.MaxDuration), minPriceForTotalSpace: config.MinPrice.TestTokens(), - maxCollateral: config.MaxCollateral.TestTokens(), - maxDuration: TimeSpan.FromSeconds(config.MaxDuration)); + maxCollateral: config.MaxCollateral.TestTokens() + ); + + var response = codexNode.Marketplace.MakeStorageAvailable(availability); if (!string.IsNullOrEmpty(response)) {