Cleanup marketplace-access methods
This commit is contained in:
parent
32f56e8213
commit
a6c2bf5230
|
@ -2,15 +2,13 @@
|
||||||
using Logging;
|
using Logging;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Utils;
|
using Utils;
|
||||||
using System.Numerics;
|
|
||||||
|
|
||||||
namespace CodexPlugin
|
namespace CodexPlugin
|
||||||
{
|
{
|
||||||
public interface IMarketplaceAccess
|
public interface IMarketplaceAccess
|
||||||
{
|
{
|
||||||
string MakeStorageAvailable(ByteSize size, TestToken minPriceForTotalSpace, TestToken maxCollateral, TimeSpan maxDuration);
|
string MakeStorageAvailable(StorageAvailability availability);
|
||||||
StoragePurchaseContract RequestStorage(ContentId contentId, TestToken pricePerSlotPerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration);
|
StoragePurchaseContract RequestStorage(StoragePurchase purchase);
|
||||||
StoragePurchaseContract RequestStorage(ContentId contentId, TestToken pricePerSlotPerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration, TimeSpan expiry);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MarketplaceAccess : IMarketplaceAccess
|
public class MarketplaceAccess : IMarketplaceAccess
|
||||||
|
@ -24,35 +22,12 @@ namespace CodexPlugin
|
||||||
this.codexAccess = codexAccess;
|
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 response = codexAccess.RequestStorage(request, purchase.ContentId.Id);
|
||||||
{
|
|
||||||
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);
|
|
||||||
|
|
||||||
if (response == "Purchasing not available" ||
|
if (response == "Purchasing not available" ||
|
||||||
response == "Expiry required" ||
|
response == "Expiry required" ||
|
||||||
|
@ -64,24 +39,13 @@ namespace CodexPlugin
|
||||||
|
|
||||||
Log($"Storage requested successfully. PurchaseId: '{response}'.");
|
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
|
availability.Log(log);
|
||||||
{
|
var request = availability.ToApiRequest();
|
||||||
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)})");
|
|
||||||
|
|
||||||
var response = codexAccess.SalesAvailability(request);
|
var response = codexAccess.SalesAvailability(request);
|
||||||
|
|
||||||
|
@ -90,18 +54,6 @@ namespace CodexPlugin
|
||||||
return response.id;
|
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)
|
private void Log(string msg)
|
||||||
{
|
{
|
||||||
log.Log($"{codexAccess.Container.Name} {msg}");
|
log.Log($"{codexAccess.Container.Name} {msg}");
|
||||||
|
@ -110,22 +62,16 @@ namespace CodexPlugin
|
||||||
|
|
||||||
public class MarketplaceUnavailable : IMarketplaceAccess
|
public class MarketplaceUnavailable : IMarketplaceAccess
|
||||||
{
|
{
|
||||||
public StoragePurchaseContract RequestStorage(ContentId contentId, TestToken pricePerBytePerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration)
|
public string MakeStorageAvailable(StorageAvailability availability)
|
||||||
{
|
|
||||||
Unavailable();
|
|
||||||
return null!;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StoragePurchaseContract RequestStorage(ContentId contentId, TestToken pricePerSlotPerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration, TimeSpan expiry)
|
|
||||||
{
|
{
|
||||||
Unavailable();
|
Unavailable();
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string MakeStorageAvailable(ByteSize size, TestToken minPricePerBytePerSecond, TestToken maxCollateral, TimeSpan duration)
|
public StoragePurchaseContract RequestStorage(StoragePurchase purchase)
|
||||||
{
|
{
|
||||||
Unavailable();
|
Unavailable();
|
||||||
return string.Empty;
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Unavailable()
|
private void Unavailable()
|
||||||
|
@ -141,16 +87,16 @@ namespace CodexPlugin
|
||||||
private readonly CodexAccess codexAccess;
|
private readonly CodexAccess codexAccess;
|
||||||
private DateTime? contractStartUtc;
|
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.log = log;
|
||||||
this.codexAccess = codexAccess;
|
this.codexAccess = codexAccess;
|
||||||
PurchaseId = purchaseId;
|
PurchaseId = purchaseId;
|
||||||
ContractDuration = contractDuration;
|
Purchase = purchase;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string PurchaseId { get; }
|
public string PurchaseId { get; }
|
||||||
public TimeSpan ContractDuration { get; }
|
public StoragePurchase Purchase { get; }
|
||||||
|
|
||||||
public void WaitForStorageContractStarted()
|
public void WaitForStorageContractStarted()
|
||||||
{
|
{
|
||||||
|
@ -165,7 +111,7 @@ namespace CodexPlugin
|
||||||
}
|
}
|
||||||
var gracePeriod = TimeSpan.FromSeconds(10);
|
var gracePeriod = TimeSpan.FromSeconds(10);
|
||||||
var currentContractTime = DateTime.UtcNow - contractStartUtc!.Value;
|
var currentContractTime = DateTime.UtcNow - contractStartUtc!.Value;
|
||||||
var timeout = (ContractDuration - currentContractTime) + gracePeriod;
|
var timeout = (Purchase.Duration - currentContractTime) + gracePeriod;
|
||||||
WaitForStorageContractState(timeout, "finished");
|
WaitForStorageContractState(timeout, "finished");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,7 +123,7 @@ namespace CodexPlugin
|
||||||
}
|
}
|
||||||
var gracePeriod = TimeSpan.FromSeconds(10);
|
var gracePeriod = TimeSpan.FromSeconds(10);
|
||||||
var currentContractTime = DateTime.UtcNow - contractStartUtc!.Value;
|
var currentContractTime = DateTime.UtcNow - contractStartUtc!.Value;
|
||||||
var timeout = (ContractDuration - currentContractTime) + gracePeriod;
|
var timeout = (Purchase.Duration - currentContractTime) + gracePeriod;
|
||||||
WaitForStorageContractState(timeout, "finished");
|
WaitForStorageContractState(timeout, "finished");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,13 +33,16 @@ namespace CodexTests.BasicTests
|
||||||
|
|
||||||
var rc = Ci.DeployMetricsCollector(nodes);
|
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)
|
foreach (var node in nodes)
|
||||||
{
|
{
|
||||||
node.Marketplace.MakeStorageAvailable(
|
node.Marketplace.MakeStorageAvailable(availability);
|
||||||
size: 500.MB(),
|
|
||||||
minPriceForTotalSpace: 500.TestTokens(),
|
|
||||||
maxCollateral: 1024.TestTokens(),
|
|
||||||
maxDuration: TimeSpan.FromMinutes(5));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var endTime = DateTime.UtcNow + TimeSpan.FromHours(10);
|
var endTime = DateTime.UtcNow + TimeSpan.FromHours(10);
|
||||||
|
|
|
@ -54,7 +54,7 @@ namespace CodexTests.BasicTests
|
||||||
public void MarketplaceExample()
|
public void MarketplaceExample()
|
||||||
{
|
{
|
||||||
var sellerInitialBalance = 234.TestTokens();
|
var sellerInitialBalance = 234.TestTokens();
|
||||||
var buyerInitialBalance = 1000.TestTokens();
|
var buyerInitialBalance = 100000.TestTokens();
|
||||||
var fileSize = 10.MB();
|
var fileSize = 10.MB();
|
||||||
|
|
||||||
var geth = Ci.StartGethNode(s => s.IsMiner().WithName("disttest-geth"));
|
var geth = Ci.StartGethNode(s => s.IsMiner().WithName("disttest-geth"));
|
||||||
|
@ -69,11 +69,14 @@ namespace CodexTests.BasicTests
|
||||||
.AsValidator()));
|
.AsValidator()));
|
||||||
|
|
||||||
AssertBalance(contracts, seller, Is.EqualTo(sellerInitialBalance));
|
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(),
|
minPriceForTotalSpace: 1.TestTokens(),
|
||||||
maxCollateral: 20.TestTokens(),
|
maxCollateral: 20.TestTokens()
|
||||||
maxDuration: TimeSpan.FromMinutes(30));
|
);
|
||||||
|
seller.Marketplace.MakeStorageAvailable(availability);
|
||||||
|
|
||||||
var testFile = GenerateTestFile(fileSize);
|
var testFile = GenerateTestFile(fileSize);
|
||||||
|
|
||||||
|
@ -85,20 +88,26 @@ namespace CodexTests.BasicTests
|
||||||
AssertBalance(contracts, buyer, Is.EqualTo(buyerInitialBalance));
|
AssertBalance(contracts, buyer, Is.EqualTo(buyerInitialBalance));
|
||||||
|
|
||||||
var contentId = buyer.UploadFile(testFile);
|
var contentId = buyer.UploadFile(testFile);
|
||||||
var purchaseContract = buyer.Marketplace.RequestStorage(contentId,
|
|
||||||
pricePerSlotPerSecond: 2.TestTokens(),
|
var purchase = new StoragePurchase(contentId)
|
||||||
requiredCollateral: 10.TestTokens(),
|
{
|
||||||
minRequiredNumberOfNodes: 1,
|
PricePerSlotPerSecond = 2.TestTokens(),
|
||||||
proofProbability: 5,
|
RequiredCollateral = 10.TestTokens(),
|
||||||
duration: TimeSpan.FromMinutes(5),
|
MinRequiredNumberOfNodes = 5,
|
||||||
expiry: TimeSpan.FromMinutes(4));
|
NodeFailureTolerance = 2,
|
||||||
|
ProofProbability = 5,
|
||||||
|
Duration = TimeSpan.FromMinutes(5),
|
||||||
|
Expiry = TimeSpan.FromMinutes(4)
|
||||||
|
};
|
||||||
|
|
||||||
|
var purchaseContract = buyer.Marketplace.RequestStorage(purchase);
|
||||||
|
|
||||||
purchaseContract.WaitForStorageContractStarted(TimeSpan.FromMinutes(4));
|
purchaseContract.WaitForStorageContractStarted(TimeSpan.FromMinutes(4));
|
||||||
|
|
||||||
AssertBalance(contracts, seller, Is.LessThan(sellerInitialBalance), "Collateral was not placed.");
|
AssertBalance(contracts, seller, Is.LessThan(sellerInitialBalance), "Collateral was not placed.");
|
||||||
|
|
||||||
var request = GetOnChainStorageRequest(contracts);
|
var request = GetOnChainStorageRequest(contracts);
|
||||||
AssertStorageRequest(request, contracts, buyer);
|
AssertStorageRequest(request, purchase, contracts, buyer);
|
||||||
AssertSlotFilledEvents(contracts, request, seller);
|
AssertSlotFilledEvents(contracts, request, seller);
|
||||||
AssertContractSlot(contracts, request, 0, seller);
|
AssertContractSlot(contracts, request, 0, seller);
|
||||||
|
|
||||||
|
@ -141,11 +150,11 @@ namespace CodexTests.BasicTests
|
||||||
Assert.That(filledSlotEvent.Host, Is.EqualTo(seller.EthAddress));
|
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(contracts.GetRequestState(request), Is.EqualTo(RequestState.Started));
|
||||||
Assert.That(request.ClientAddress, Is.EqualTo(buyer.EthAddress));
|
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)
|
private Request GetOnChainStorageRequest(ICodexContracts contracts)
|
||||||
|
|
|
@ -67,11 +67,14 @@ namespace CodexNetDeployer
|
||||||
|
|
||||||
if (config.ShouldMakeStorageAvailable)
|
if (config.ShouldMakeStorageAvailable)
|
||||||
{
|
{
|
||||||
var response = codexNode.Marketplace.MakeStorageAvailable(
|
var availability = new StorageAvailability(
|
||||||
size: config.StorageSell!.Value.MB(),
|
totalSpace: config.StorageSell!.Value.MB(),
|
||||||
|
maxDuration: TimeSpan.FromSeconds(config.MaxDuration),
|
||||||
minPriceForTotalSpace: config.MinPrice.TestTokens(),
|
minPriceForTotalSpace: config.MinPrice.TestTokens(),
|
||||||
maxCollateral: config.MaxCollateral.TestTokens(),
|
maxCollateral: config.MaxCollateral.TestTokens()
|
||||||
maxDuration: TimeSpan.FromSeconds(config.MaxDuration));
|
);
|
||||||
|
|
||||||
|
var response = codexNode.Marketplace.MakeStorageAvailable(availability);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(response))
|
if (!string.IsNullOrEmpty(response))
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue