Finishes implementation of marketplace support
This commit is contained in:
parent
e36d910f2f
commit
12d122ad83
|
@ -26,6 +26,16 @@ namespace DistTestCore.Codex
|
|||
return Http().HttpGetStream("download/" + contentId);
|
||||
}
|
||||
|
||||
public CodexSalesAvailabilityResponse SalesAvailability(CodexSalesAvailabilityRequest request)
|
||||
{
|
||||
return Http().HttpPostJson<CodexSalesAvailabilityRequest, CodexSalesAvailabilityResponse>("sales/availability", request);
|
||||
}
|
||||
|
||||
public CodexSalesRequestStorageResponse RequestStorage(CodexSalesRequestStorageRequest request, string contentId)
|
||||
{
|
||||
return Http().HttpPostJson<CodexSalesRequestStorageRequest, CodexSalesRequestStorageResponse>($"storage/request/{contentId}", request);
|
||||
}
|
||||
|
||||
private Http Http()
|
||||
{
|
||||
var ip = Container.Pod.Cluster.IP;
|
||||
|
@ -53,4 +63,37 @@ namespace DistTestCore.Codex
|
|||
public string version { get; set; } = string.Empty;
|
||||
public string revision { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class CodexSalesAvailabilityRequest
|
||||
{
|
||||
public string size { get; set; } = string.Empty;
|
||||
public string duration { get; set; } = string.Empty;
|
||||
public string minPrice { get; set; } = string.Empty;
|
||||
public string maxCollateral { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class CodexSalesAvailabilityResponse
|
||||
{
|
||||
public string id { get; set; } = string.Empty;
|
||||
public string size { get; set; } = string.Empty;
|
||||
public string duration { get; set; } = string.Empty;
|
||||
public string minPrice { get; set; } = string.Empty;
|
||||
public string maxCollateral { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class CodexSalesRequestStorageRequest
|
||||
{
|
||||
public string duration { get; set; } = string.Empty;
|
||||
public string proofProbability { get; set; } = string.Empty;
|
||||
public string reward { get; set; } = string.Empty;
|
||||
public string collateral { get; set; } = string.Empty;
|
||||
public string? expiry { get; set; }
|
||||
public uint? nodes { get; set; }
|
||||
public uint? tolerance { get; set;}
|
||||
}
|
||||
|
||||
public class CodexSalesRequestStorageResponse
|
||||
{
|
||||
public string purchaseId { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace DistTestCore
|
|||
private void TransferInitialBalance(MarketplaceNetwork marketplaceNetwork, MarketplaceInitialConfig marketplaceConfig, GethCompanionNodeInfo[] companionNodes)
|
||||
{
|
||||
var interaction = marketplaceNetwork.StartInteraction(lifecycle.Log);
|
||||
var tokenAddress = interaction.GetTokenAddress(marketplaceNetwork.Marketplace.Address);
|
||||
var tokenAddress = marketplaceNetwork.Marketplace.TokenAddress;
|
||||
|
||||
foreach (var node in companionNodes)
|
||||
{
|
||||
|
@ -79,7 +79,7 @@ namespace DistTestCore
|
|||
if (network == null)
|
||||
{
|
||||
var bootstrapInfo = bootstrapNodeStarter.StartGethBootstrapNode();
|
||||
var marketplaceInfo = codexContractsStarter.Start(bootstrapInfo.RunningContainers.Containers[0]);
|
||||
var marketplaceInfo = codexContractsStarter.Start(bootstrapInfo);
|
||||
network = new MarketplaceNetwork(bootstrapInfo, marketplaceInfo );
|
||||
}
|
||||
return network;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using Newtonsoft.Json;
|
||||
using NUnit.Framework;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Http.Json;
|
||||
using Utils;
|
||||
|
||||
namespace DistTestCore
|
||||
|
@ -37,6 +38,19 @@ namespace DistTestCore
|
|||
return JsonConvert.DeserializeObject<T>(HttpGetString(route))!;
|
||||
}
|
||||
|
||||
public TResponse HttpPostJson<TRequest, TResponse>(string route, TRequest body)
|
||||
{
|
||||
return Retry(() =>
|
||||
{
|
||||
using var client = GetClient();
|
||||
var url = GetUrl() + route;
|
||||
using var content = JsonContent.Create(body);
|
||||
var result = Time.Wait(client.PostAsync(url, content));
|
||||
var json = Time.Wait(result.Content.ReadAsStringAsync());
|
||||
return JsonConvert.DeserializeObject<TResponse>(json)!;
|
||||
});
|
||||
}
|
||||
|
||||
public string HttpPostStream(string route, Stream stream)
|
||||
{
|
||||
return Retry(() =>
|
||||
|
|
|
@ -12,12 +12,12 @@ namespace DistTestCore.Marketplace
|
|||
{
|
||||
}
|
||||
|
||||
public MarketplaceInfo Start(RunningContainer bootstrapContainer)
|
||||
public MarketplaceInfo Start(GethBootstrapNodeInfo bootstrapNode)
|
||||
{
|
||||
LogStart("Deploying Codex contracts...");
|
||||
|
||||
var workflow = workflowCreator.CreateWorkflow();
|
||||
var startupConfig = CreateStartupConfig(bootstrapContainer);
|
||||
var startupConfig = CreateStartupConfig(bootstrapNode.RunningContainers.Containers[0]);
|
||||
|
||||
var containers = workflow.Start(1, Location.Unspecified, new CodexContractsContainerRecipe(), startupConfig);
|
||||
if (containers.Containers.Length != 1) throw new InvalidOperationException("Expected 1 Codex contracts container to be created. Test infra failure.");
|
||||
|
@ -33,9 +33,12 @@ namespace DistTestCore.Marketplace
|
|||
var extractor = new ContainerInfoExtractor(workflow, container);
|
||||
var marketplaceAddress = extractor.ExtractMarketplaceAddress();
|
||||
|
||||
var interaction = bootstrapNode.StartInteraction(lifecycle.Log);
|
||||
var tokenAddress = interaction.GetTokenAddress(marketplaceAddress);
|
||||
|
||||
LogEnd("Contracts deployed.");
|
||||
|
||||
return new MarketplaceInfo(marketplaceAddress);
|
||||
return new MarketplaceInfo(marketplaceAddress, tokenAddress);
|
||||
}
|
||||
|
||||
private void WaitUntil(Func<bool> predicate)
|
||||
|
@ -54,12 +57,14 @@ namespace DistTestCore.Marketplace
|
|||
|
||||
public class MarketplaceInfo
|
||||
{
|
||||
public MarketplaceInfo(string address)
|
||||
public MarketplaceInfo(string address, string tokenAddress)
|
||||
{
|
||||
Address = address;
|
||||
TokenAddress = tokenAddress;
|
||||
}
|
||||
|
||||
public string Address { get; }
|
||||
public string TokenAddress { get; }
|
||||
}
|
||||
|
||||
public class ContractsReadyLogHandler : LogHandler
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
using KubernetesWorkflow;
|
||||
using Logging;
|
||||
using NethereumWorkflow;
|
||||
|
||||
namespace DistTestCore.Marketplace
|
||||
{
|
||||
|
@ -20,5 +22,16 @@ namespace DistTestCore.Marketplace
|
|||
public string PubKey { get; }
|
||||
public string PrivateKey { get; }
|
||||
public Port DiscoveryPort { get; }
|
||||
|
||||
public NethereumInteraction StartInteraction(TestLog log)
|
||||
{
|
||||
var ip = RunningContainers.RunningPod.Cluster.IP;
|
||||
var port = RunningContainers.Containers[0].ServicePorts[0].Number;
|
||||
var account = Account;
|
||||
var privateKey = PrivateKey;
|
||||
|
||||
var creator = new NethereumInteractionCreator(log, ip, port, account, privateKey);
|
||||
return creator.CreateWorkflow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
using Logging;
|
||||
using DistTestCore.Codex;
|
||||
using Logging;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Constraints;
|
||||
using System.Numerics;
|
||||
|
||||
namespace DistTestCore.Marketplace
|
||||
{
|
||||
public interface IMarketplaceAccess
|
||||
{
|
||||
void MakeStorageAvailable(ByteSize size, int minPricePerBytePerSecond, float maxCollateral);
|
||||
void RequestStorage(ContentId contentId, int pricePerBytePerSecond, float requiredCollateral, float minRequiredNumberOfNodes);
|
||||
string MakeStorageAvailable(ByteSize size, TestToken minPricePerBytePerSecond, TestToken maxCollateral, TimeSpan maxDuration);
|
||||
string RequestStorage(ContentId contentId, TestToken pricePerBytePerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration);
|
||||
void AssertThatBalance(IResolveConstraint constraint, string message = "");
|
||||
decimal GetBalance();
|
||||
}
|
||||
|
@ -17,22 +19,58 @@ namespace DistTestCore.Marketplace
|
|||
private readonly TestLog log;
|
||||
private readonly MarketplaceNetwork marketplaceNetwork;
|
||||
private readonly GethCompanionNodeInfo companionNode;
|
||||
private readonly CodexAccess codexAccess;
|
||||
|
||||
public MarketplaceAccess(TestLog log, MarketplaceNetwork marketplaceNetwork, GethCompanionNodeInfo companionNode)
|
||||
public MarketplaceAccess(TestLog log, MarketplaceNetwork marketplaceNetwork, GethCompanionNodeInfo companionNode, CodexAccess codexAccess)
|
||||
{
|
||||
this.log = log;
|
||||
this.marketplaceNetwork = marketplaceNetwork;
|
||||
this.companionNode = companionNode;
|
||||
this.codexAccess = codexAccess;
|
||||
}
|
||||
|
||||
public void RequestStorage(ContentId contentId, int pricePerBytePerSecond, float requiredCollateral, float minRequiredNumberOfNodes)
|
||||
public string RequestStorage(ContentId contentId, TestToken pricePerBytePerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
var request = new CodexSalesRequestStorageRequest
|
||||
{
|
||||
duration = ToHexBigInt(duration.TotalSeconds),
|
||||
proofProbability = ToHexBigInt(proofProbability),
|
||||
reward = ToHexBigInt(pricePerBytePerSecond),
|
||||
collateral = ToHexBigInt(requiredCollateral),
|
||||
expiry = null,
|
||||
nodes = minRequiredNumberOfNodes,
|
||||
tolerance = null,
|
||||
};
|
||||
|
||||
var response = codexAccess.RequestStorage(request, contentId.Id);
|
||||
|
||||
return response.purchaseId;
|
||||
}
|
||||
|
||||
public void MakeStorageAvailable(ByteSize size, int minPricePerBytePerSecond, float maxCollateral)
|
||||
public string MakeStorageAvailable(ByteSize size, TestToken minPricePerBytePerSecond, TestToken maxCollateral, TimeSpan duration)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
var request = new CodexSalesAvailabilityRequest
|
||||
{
|
||||
size = ToHexBigInt(size.SizeInBytes),
|
||||
duration = ToHexBigInt(duration.TotalSeconds),
|
||||
maxCollateral = ToHexBigInt(maxCollateral),
|
||||
minPrice = ToHexBigInt(minPricePerBytePerSecond)
|
||||
};
|
||||
|
||||
var response = codexAccess.SalesAvailability(request);
|
||||
|
||||
return response.id;
|
||||
}
|
||||
|
||||
private string ToHexBigInt(double d)
|
||||
{
|
||||
return "0x" + string.Format("{0:X}", Convert.ToInt64(d));
|
||||
}
|
||||
|
||||
public string ToHexBigInt(TestToken t)
|
||||
{
|
||||
var bigInt = new BigInteger(t.Amount);
|
||||
return "0x" + bigInt.ToString("X");
|
||||
}
|
||||
|
||||
public void AssertThatBalance(IResolveConstraint constraint, string message = "")
|
||||
|
@ -43,20 +81,22 @@ namespace DistTestCore.Marketplace
|
|||
public decimal GetBalance()
|
||||
{
|
||||
var interaction = marketplaceNetwork.StartInteraction(log);
|
||||
return interaction.GetBalance(companionNode.Account);
|
||||
return interaction.GetBalance(marketplaceNetwork.Marketplace.TokenAddress, companionNode.Account);
|
||||
}
|
||||
}
|
||||
|
||||
public class MarketplaceUnavailable : IMarketplaceAccess
|
||||
{
|
||||
public void RequestStorage(ContentId contentId, int pricePerBytePerSecond, float requiredCollateral, float minRequiredNumberOfNodes)
|
||||
public string RequestStorage(ContentId contentId, TestToken pricePerBytePerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration)
|
||||
{
|
||||
Unavailable();
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public void MakeStorageAvailable(ByteSize size, int minPricePerBytePerSecond, float maxCollateral)
|
||||
public string MakeStorageAvailable(ByteSize size, TestToken minPricePerBytePerSecond, TestToken maxCollateral, TimeSpan duration)
|
||||
{
|
||||
Unavailable();
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public void AssertThatBalance(IResolveConstraint constraint, string message = "")
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace DistTestCore.Marketplace
|
|||
public IMarketplaceAccess CreateMarketplaceAccess(CodexAccess access)
|
||||
{
|
||||
var companionNode = GetGethCompanionNode(access);
|
||||
return new MarketplaceAccess(log, marketplaceNetwork, companionNode);
|
||||
return new MarketplaceAccess(log, marketplaceNetwork, companionNode, access);
|
||||
}
|
||||
|
||||
private GethCompanionNodeInfo GetGethCompanionNode(CodexAccess access)
|
||||
|
|
|
@ -16,13 +16,7 @@ namespace DistTestCore.Marketplace
|
|||
|
||||
public NethereumInteraction StartInteraction(TestLog log)
|
||||
{
|
||||
var ip = Bootstrap.RunningContainers.RunningPod.Cluster.IP;
|
||||
var port = Bootstrap.RunningContainers.Containers[0].ServicePorts[0].Number;
|
||||
var account = Bootstrap.Account;
|
||||
var privateKey = Bootstrap.PrivateKey;
|
||||
|
||||
var creator = new NethereumInteractionCreator(log, ip, port, account, privateKey);
|
||||
return creator.CreateWorkflow();
|
||||
return Bootstrap.StartInteraction(log);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,10 +56,16 @@ namespace NethereumWorkflow
|
|||
Time.Wait(handler.SendRequestAndWaitForReceiptAsync(tokenAddress, function));
|
||||
}
|
||||
|
||||
public decimal GetBalance(string account)
|
||||
public decimal GetBalance(string tokenAddress, string account)
|
||||
{
|
||||
var bigInt = Time.Wait(web3.Eth.GetBalance.SendRequestAsync(account));
|
||||
var result = ToDecimal(bigInt);
|
||||
var function = new GetTokenBalanceFunction
|
||||
{
|
||||
Owner = account
|
||||
};
|
||||
|
||||
var handler = web3.Eth.GetContractQueryHandler<GetTokenBalanceFunction>();
|
||||
var result = ToDecimal(Time.Wait(handler.QueryAsync<BigInteger>(tokenAddress, function)));
|
||||
|
||||
Log($"Balance of {account} is {result}");
|
||||
return result;
|
||||
}
|
||||
|
@ -81,6 +87,11 @@ namespace NethereumWorkflow
|
|||
return (decimal)hexBigInteger.Value;
|
||||
}
|
||||
|
||||
private decimal ToDecimal(BigInteger bigInteger)
|
||||
{
|
||||
return (decimal)bigInteger;
|
||||
}
|
||||
|
||||
private void Log(string msg)
|
||||
{
|
||||
log.Log(msg);
|
||||
|
@ -101,4 +112,11 @@ namespace NethereumWorkflow
|
|||
[Parameter("uint256", "amount", 2)]
|
||||
public BigInteger Amount { get; set; }
|
||||
}
|
||||
|
||||
[Function("balanceOf", "uint256")]
|
||||
public class GetTokenBalanceFunction :FunctionMessage
|
||||
{
|
||||
[Parameter("address", "owner", 1)]
|
||||
public string Owner { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using DistTestCore;
|
||||
using DistTestCore.Codex;
|
||||
using NUnit.Framework;
|
||||
using Utils;
|
||||
|
||||
namespace Tests.BasicTests
|
||||
{
|
||||
|
@ -49,38 +50,44 @@ namespace Tests.BasicTests
|
|||
[Test]
|
||||
public void MarketplaceExample()
|
||||
{
|
||||
var group = SetupCodexNodes(2)
|
||||
var primary = SetupCodexNodes(1)
|
||||
.WithStorageQuota(10.GB())
|
||||
.EnableMarketplace(20.TestTokens())
|
||||
.BringOnline();
|
||||
.EnableMarketplace(initialBalance: 234.TestTokens())
|
||||
.BringOnline()[0];
|
||||
|
||||
foreach (var node in group)
|
||||
{
|
||||
Assert.That(node.Marketplace.GetBalance(), Is.EqualTo(20));
|
||||
}
|
||||
Assert.That(primary.Marketplace.GetBalance(), Is.EqualTo(234));
|
||||
|
||||
// WIP: Balance is now only ETH.
|
||||
// todo: All nodes should have plenty of ETH to pay for transactions.
|
||||
// todo: Upload our own token, use this exclusively. ETH should be invisibile to the tests.
|
||||
var secondary = SetupCodexNodes(1)
|
||||
.EnableMarketplace(initialBalance: 1000.TestTokens())
|
||||
.BringOnline()[0];
|
||||
|
||||
primary.ConnectToPeer(secondary);
|
||||
|
||||
//var secondary = SetupCodexNodes(1)
|
||||
// .EnableMarketplace(initialBalance: 1000)
|
||||
// .BringOnline()[0];
|
||||
// Gives 503 - Persistance not enabled in current codex image.
|
||||
primary.Marketplace.MakeStorageAvailable(
|
||||
size: 10.GB(),
|
||||
minPricePerBytePerSecond: 1.TestTokens(),
|
||||
maxCollateral: 20.TestTokens(),
|
||||
maxDuration: TimeSpan.FromMinutes(3));
|
||||
|
||||
//primary.ConnectToPeer(secondary);
|
||||
//primary.Marketplace.MakeStorageAvailable(10.GB(), minPricePerBytePerSecond: 1, maxCollateral: 20);
|
||||
var testFile = GenerateTestFile(10.MB());
|
||||
var contentId = secondary.UploadFile(testFile);
|
||||
|
||||
//var testFile = GenerateTestFile(10.MB());
|
||||
//var contentId = secondary.UploadFile(testFile);
|
||||
//secondary.Marketplace.RequestStorage(contentId, pricePerBytePerSecond: 2,
|
||||
// requiredCollateral: 10, minRequiredNumberOfNodes: 1);
|
||||
// Gives 500 - Persistance not enabled in current codex image.
|
||||
secondary.Marketplace.RequestStorage(contentId,
|
||||
pricePerBytePerSecond: 2.TestTokens(),
|
||||
requiredCollateral: 10.TestTokens(),
|
||||
minRequiredNumberOfNodes: 1,
|
||||
proofProbability: 5,
|
||||
duration: TimeSpan.FromMinutes(2));
|
||||
|
||||
//primary.Marketplace.AssertThatBalance(Is.LessThan(20), "Collateral was not placed.");
|
||||
//var primaryBalance = primary.Marketplace.GetBalance();
|
||||
Time.Sleep(TimeSpan.FromMinutes(3));
|
||||
|
||||
//secondary.Marketplace.AssertThatBalance(Is.LessThan(1000), "Contractor was not charged for storage.");
|
||||
//primary.Marketplace.AssertThatBalance(Is.GreaterThan(primaryBalance), "Storer was not paid for storage.");
|
||||
primary.Marketplace.AssertThatBalance(Is.LessThan(20), "Collateral was not placed.");
|
||||
var primaryBalance = primary.Marketplace.GetBalance();
|
||||
|
||||
secondary.Marketplace.AssertThatBalance(Is.LessThan(1000), "Contractor was not charged for storage.");
|
||||
primary.Marketplace.AssertThatBalance(Is.GreaterThan(primaryBalance), "Storer was not paid for storage.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue