2023-04-18 13:33:12 +00:00
using DistTestCore.Codex ;
2023-06-01 14:28:34 +00:00
using DistTestCore.Helpers ;
2023-07-31 07:56:37 +00:00
using Logging ;
using Newtonsoft.Json ;
2023-04-14 08:51:35 +00:00
using NUnit.Framework ;
using NUnit.Framework.Constraints ;
2023-04-18 13:33:12 +00:00
using System.Numerics ;
2023-04-19 07:57:37 +00:00
using Utils ;
2023-04-14 08:51:35 +00:00
namespace DistTestCore.Marketplace
{
public interface IMarketplaceAccess
{
2023-04-18 13:33:12 +00:00
string MakeStorageAvailable ( ByteSize size , TestToken minPricePerBytePerSecond , TestToken maxCollateral , TimeSpan maxDuration ) ;
2023-07-31 07:56:37 +00:00
StoragePurchaseContract RequestStorage ( ContentId contentId , TestToken pricePerSlotPerSecond , TestToken requiredCollateral , uint minRequiredNumberOfNodes , int proofProbability , TimeSpan duration ) ;
2023-04-14 08:51:35 +00:00
void AssertThatBalance ( IResolveConstraint constraint , string message = "" ) ;
2023-04-19 07:57:37 +00:00
TestToken GetBalance ( ) ;
2023-04-14 08:51:35 +00:00
}
public class MarketplaceAccess : IMarketplaceAccess
{
2023-06-01 07:35:18 +00:00
private readonly TestLifecycle lifecycle ;
2023-04-18 08:22:11 +00:00
private readonly MarketplaceNetwork marketplaceNetwork ;
2023-05-03 08:21:15 +00:00
private readonly GethAccount account ;
2023-04-18 13:33:12 +00:00
private readonly CodexAccess codexAccess ;
2023-04-14 08:51:35 +00:00
2023-06-01 07:35:18 +00:00
public MarketplaceAccess ( TestLifecycle lifecycle , MarketplaceNetwork marketplaceNetwork , GethAccount account , CodexAccess codexAccess )
2023-04-14 08:51:35 +00:00
{
2023-06-01 07:35:18 +00:00
this . lifecycle = lifecycle ;
2023-04-18 08:22:11 +00:00
this . marketplaceNetwork = marketplaceNetwork ;
2023-04-26 09:12:33 +00:00
this . account = account ;
2023-04-18 13:33:12 +00:00
this . codexAccess = codexAccess ;
2023-04-14 08:51:35 +00:00
}
2023-07-31 07:56:37 +00:00
public StoragePurchaseContract RequestStorage ( ContentId contentId , TestToken pricePerSlotPerSecond , TestToken requiredCollateral , uint minRequiredNumberOfNodes , int proofProbability , TimeSpan duration )
2023-04-14 08:51:35 +00:00
{
2023-04-18 13:33:12 +00:00
var request = new CodexSalesRequestStorageRequest
{
2023-07-12 12:53:27 +00:00
duration = ToDecInt ( duration . TotalSeconds ) ,
proofProbability = ToDecInt ( proofProbability ) ,
reward = ToDecInt ( pricePerSlotPerSecond ) ,
collateral = ToDecInt ( requiredCollateral ) ,
2023-04-18 13:33:12 +00:00
expiry = null ,
nodes = minRequiredNumberOfNodes ,
tolerance = null ,
} ;
2023-04-19 07:57:37 +00:00
Log ( $"Requesting storage for: {contentId.Id}... (" +
2023-06-27 13:28:00 +00:00
$"pricePerSlotPerSecond: {pricePerSlotPerSecond}, " +
2023-04-19 08:42:08 +00:00
$"requiredCollateral: {requiredCollateral}, " +
$"minRequiredNumberOfNodes: {minRequiredNumberOfNodes}, " +
$"proofProbability: {proofProbability}, " +
2023-04-19 07:57:37 +00:00
$"duration: {Time.FormatDuration(duration)})" ) ;
2023-06-29 14:07:49 +00:00
var response = codexAccess . RequestStorage ( request , contentId . Id ) ;
2023-04-18 13:33:12 +00:00
2023-04-24 14:07:32 +00:00
if ( response = = "Purchasing not available" )
{
throw new InvalidOperationException ( response ) ;
}
2023-07-31 07:56:37 +00:00
Log ( $"Storage requested successfully. PurchaseId: '{response}'." ) ;
2023-04-19 07:57:37 +00:00
2023-07-31 07:56:37 +00:00
return new StoragePurchaseContract ( lifecycle . Log , codexAccess , response , duration ) ;
2023-04-18 13:33:12 +00:00
}
2023-06-28 06:48:46 +00:00
public string MakeStorageAvailable ( ByteSize totalSpace , TestToken minPriceForTotalSpace , TestToken maxCollateral , TimeSpan maxDuration )
2023-04-18 13:33:12 +00:00
{
var request = new CodexSalesAvailabilityRequest
{
2023-06-29 07:09:20 +00:00
size = ToDecInt ( totalSpace . SizeInBytes ) ,
2023-07-12 12:53:27 +00:00
duration = ToDecInt ( maxDuration . TotalSeconds ) ,
maxCollateral = ToDecInt ( maxCollateral ) ,
2023-06-29 07:09:20 +00:00
minPrice = ToDecInt ( minPriceForTotalSpace )
2023-04-18 13:33:12 +00:00
} ;
2023-04-19 07:57:37 +00:00
Log ( $"Making storage available... (" +
2023-06-28 06:48:46 +00:00
$"size: {totalSpace}, " +
2023-07-13 08:33:31 +00:00
$"minPriceForTotalSpace: {minPriceForTotalSpace}, " +
2023-04-19 08:42:08 +00:00
$"maxCollateral: {maxCollateral}, " +
$"maxDuration: {Time.FormatDuration(maxDuration)})" ) ;
2023-04-19 07:57:37 +00:00
2023-06-29 14:07:49 +00:00
var response = codexAccess . SalesAvailability ( request ) ;
2023-04-18 13:33:12 +00:00
2023-04-19 07:57:37 +00:00
Log ( $"Storage successfully made available. Id: {response.id}" ) ;
2023-04-18 13:33:12 +00:00
return response . id ;
}
2023-07-12 12:53:27 +00:00
private string ToDecInt ( double d )
2023-04-18 13:33:12 +00:00
{
2023-07-12 12:53:27 +00:00
var i = new BigInteger ( d ) ;
return i . ToString ( "D" ) ;
2023-04-14 08:51:35 +00:00
}
2023-07-12 12:53:27 +00:00
public string ToDecInt ( TestToken t )
2023-04-14 08:51:35 +00:00
{
2023-07-12 12:53:27 +00:00
var i = new BigInteger ( t . Amount ) ;
return i . ToString ( "D" ) ;
2023-04-14 08:51:35 +00:00
}
public void AssertThatBalance ( IResolveConstraint constraint , string message = "" )
{
2023-06-01 14:28:34 +00:00
AssertHelpers . RetryAssert ( constraint , GetBalance , message ) ;
2023-04-14 08:51:35 +00:00
}
2023-04-19 07:57:37 +00:00
public TestToken GetBalance ( )
2023-04-14 08:51:35 +00:00
{
2023-06-01 07:35:18 +00:00
var interaction = marketplaceNetwork . StartInteraction ( lifecycle ) ;
2023-04-26 09:12:33 +00:00
var amount = interaction . GetBalance ( marketplaceNetwork . Marketplace . TokenAddress , account . Account ) ;
2023-04-19 07:57:37 +00:00
var balance = new TestToken ( amount ) ;
2023-04-26 09:12:33 +00:00
Log ( $"Balance of {account.Account} is {balance}." ) ;
2023-04-19 07:57:37 +00:00
return balance ;
}
private void Log ( string msg )
{
2023-06-01 07:35:18 +00:00
lifecycle . Log . Log ( $"{codexAccess.Container.Name} {msg}" ) ;
2023-04-14 08:51:35 +00:00
}
}
public class MarketplaceUnavailable : IMarketplaceAccess
{
2023-07-31 07:56:37 +00:00
public StoragePurchaseContract RequestStorage ( ContentId contentId , TestToken pricePerBytePerSecond , TestToken requiredCollateral , uint minRequiredNumberOfNodes , int proofProbability , TimeSpan duration )
2023-04-14 08:51:35 +00:00
{
Unavailable ( ) ;
2023-07-31 07:56:37 +00:00
return null ! ;
2023-04-14 08:51:35 +00:00
}
2023-04-18 13:33:12 +00:00
public string MakeStorageAvailable ( ByteSize size , TestToken minPricePerBytePerSecond , TestToken maxCollateral , TimeSpan duration )
2023-04-14 08:51:35 +00:00
{
Unavailable ( ) ;
2023-04-18 13:33:12 +00:00
return string . Empty ;
2023-04-14 08:51:35 +00:00
}
public void AssertThatBalance ( IResolveConstraint constraint , string message = "" )
{
Unavailable ( ) ;
}
2023-04-19 07:57:37 +00:00
public TestToken GetBalance ( )
2023-04-14 08:51:35 +00:00
{
Unavailable ( ) ;
2023-04-19 07:57:37 +00:00
return new TestToken ( 0 ) ;
2023-04-14 08:51:35 +00:00
}
private void Unavailable ( )
{
Assert . Fail ( "Incorrect test setup: Marketplace was not enabled for this group of Codex nodes. Add 'EnableMarketplace(...)' after 'SetupCodexNodes()' to enable it." ) ;
throw new InvalidOperationException ( ) ;
}
}
2023-07-31 07:56:37 +00:00
public class StoragePurchaseContract
{
private readonly BaseLog log ;
private readonly CodexAccess codexAccess ;
private DateTime ? contractStartUtc ;
public StoragePurchaseContract ( BaseLog log , CodexAccess codexAccess , string purchaseId , TimeSpan contractDuration )
{
this . log = log ;
this . codexAccess = codexAccess ;
PurchaseId = purchaseId ;
ContractDuration = contractDuration ;
}
public string PurchaseId { get ; }
public TimeSpan ContractDuration { get ; }
public void WaitForStorageContractStarted ( )
{
WaitForStorageContractStarted ( TimeSpan . FromSeconds ( 30 ) ) ;
}
public void WaitForStorageContractFinished ( )
{
if ( ! contractStartUtc . HasValue )
{
WaitForStorageContractStarted ( ) ;
}
var gracePeriod = TimeSpan . FromSeconds ( 10 ) ;
var currentContractTime = DateTime . UtcNow - contractStartUtc ! . Value ;
var timeout = ( ContractDuration - currentContractTime ) + gracePeriod ;
WaitForStorageContractState ( timeout , "finished" ) ;
}
/// <summary>
/// Wait for contract to start. Max timeout depends on contract filesize. Allows more time for larger files.
/// </summary>
public void WaitForStorageContractStarted ( ByteSize contractFileSize )
{
var filesizeInMb = contractFileSize . SizeInBytes / ( 1024 * 1024 ) ;
var maxWaitTime = TimeSpan . FromSeconds ( filesizeInMb * 10.0 ) ;
WaitForStorageContractStarted ( maxWaitTime ) ;
}
public void WaitForStorageContractStarted ( TimeSpan timeout )
{
WaitForStorageContractState ( timeout , "started" ) ;
contractStartUtc = DateTime . UtcNow ;
}
private void WaitForStorageContractState ( TimeSpan timeout , string desiredState )
{
var lastState = "" ;
var waitStart = DateTime . UtcNow ;
log . Log ( $"Waiting for {Time.FormatDuration(timeout)} for contract '{PurchaseId}' to reach state '{desiredState}'." ) ;
while ( lastState ! = desiredState )
{
var purchaseStatus = codexAccess . GetPurchaseStatus ( PurchaseId ) ;
var statusJson = JsonConvert . SerializeObject ( purchaseStatus ) ;
if ( purchaseStatus ! = null & & purchaseStatus . state ! = lastState )
{
lastState = purchaseStatus . state ;
log . Debug ( "Purchase status: " + statusJson ) ;
}
Thread . Sleep ( 1000 ) ;
if ( lastState = = "errored" )
{
Assert . Fail ( "Contract errored: " + statusJson ) ;
}
if ( DateTime . UtcNow - waitStart > timeout )
{
Assert . Fail ( $"Contract did not reach '{desiredState}' within timeout. {statusJson}" ) ;
}
}
log . Log ( $"Contract '{desiredState}'." ) ;
}
public CodexStoragePurchase GetPurchaseStatus ( string purchaseId )
{
return codexAccess . GetPurchaseStatus ( purchaseId ) ;
}
}
2023-04-14 08:51:35 +00:00
}