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

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

View File

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

View File

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