Cleanup marketplace-access methods

This commit is contained in:
Ben 2024-03-20 11:11:41 +01:00
parent 32f56e8213
commit a6c2bf5230
No known key found for this signature in database
GPG Key ID: 541B9D8C9F1426A1
5 changed files with 158 additions and 96 deletions

View File

@ -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");
}

View File

@ -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");
}
}
}

View File

@ -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);

View File

@ -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)

View File

@ -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))
{