diff --git a/ProjectPlugins/CodexContractsPlugin/CodexContractsAccess.cs b/ProjectPlugins/CodexContractsPlugin/CodexContractsAccess.cs index 8e2c04a..2537d33 100644 --- a/ProjectPlugins/CodexContractsPlugin/CodexContractsAccess.cs +++ b/ProjectPlugins/CodexContractsPlugin/CodexContractsAccess.cs @@ -25,7 +25,7 @@ namespace CodexContractsPlugin ICodexContractsEvents GetEvents(BlockInterval blockInterval); EthAddress? GetSlotHost(Request storageRequest, decimal slotIndex); RequestState GetRequestState(Request request); - void WithdrawFunds(string purchaseId, EthAddress address); + void WaitUntilNextPeriod(); } [JsonConverter(typeof(StringEnumConverter))] @@ -116,24 +116,13 @@ namespace CodexContractsPlugin return gethNode.Call(Deployment.MarketplaceAddress, func); } - public void WithdrawFunds(string purchaseId, EthAddress address) + public void WaitUntilNextPeriod() { - try - { - log.Log("withdrawing funds...."); - var func = new WithdrawFundsFunction - { - RequestId = purchaseId.HexToByteArray(), - FromAddress = address.Address - }; - var response = gethNode.Call(Deployment.MarketplaceAddress, func); - - log.Log("got response: " + response); - } - catch (Exception ex) - { - log.Log("Got exception: " + ex); - } + log.Log("Waiting until next proof period..."); + var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); + var periodSeconds = (int)Deployment.Config.Proofs.Period; + var secondsLeft = now % periodSeconds; + Thread.Sleep(TimeSpan.FromSeconds(secondsLeft + 1)); } private ContractInteractions StartInteraction() diff --git a/ProjectPlugins/CodexPlugin/CodexPlugin.cs b/ProjectPlugins/CodexPlugin/CodexPlugin.cs index 9dba163..04c2d5f 100644 --- a/ProjectPlugins/CodexPlugin/CodexPlugin.cs +++ b/ProjectPlugins/CodexPlugin/CodexPlugin.cs @@ -20,7 +20,7 @@ namespace CodexPlugin public void Announce() { - tools.GetLog().Log($"Loaded with Codex ID: '{codexStarter.GetCodexId()}' - Revision: {codexStarter.GetCodexRevision()}"); + Log($"Loaded with Codex ID: '{codexStarter.GetCodexId()}' - Revision: {codexStarter.GetCodexRevision()}"); } public void AddMetadata(IAddMetadata metadata) @@ -55,6 +55,10 @@ namespace CodexPlugin { mconfig.GethNode.SendEth(node, mconfig.MarketplaceSetup.InitialEth); mconfig.CodexContracts.MintTestTokens(node, mconfig.MarketplaceSetup.InitialTestTokens); + + Log($"Send {mconfig.MarketplaceSetup.InitialEth} and " + + $"minted {mconfig.MarketplaceSetup.InitialTestTokens} for " + + $"{node.GetName()} (address: {node.EthAddress})"); } } @@ -70,5 +74,10 @@ namespace CodexPlugin setup(codexSetup); return codexSetup; } + + private void Log(string msg) + { + tools.GetLog().Log(msg); + } } } diff --git a/ProjectPlugins/CodexPlugin/StoragePurchaseContract.cs b/ProjectPlugins/CodexPlugin/StoragePurchaseContract.cs index 61f6f59..e4325ca 100644 --- a/ProjectPlugins/CodexPlugin/StoragePurchaseContract.cs +++ b/ProjectPlugins/CodexPlugin/StoragePurchaseContract.cs @@ -1,4 +1,6 @@ -using CodexPlugin.Hooks; +using CodexContractsPlugin; +using CodexPlugin.Hooks; +using GethPlugin; using Logging; using Newtonsoft.Json; using Utils; @@ -12,7 +14,7 @@ namespace CodexPlugin ContentId ContentId { get; } void WaitForStorageContractSubmitted(); void WaitForStorageContractStarted(); - void WaitForStorageContractFinished(); + void WaitForStorageContractFinished(ICodexContracts contracts); } public class StoragePurchaseContract : IStoragePurchaseContract @@ -62,7 +64,7 @@ namespace CodexPlugin AssertDuration(SubmittedToStarted, timeout, nameof(SubmittedToStarted)); } - public void WaitForStorageContractFinished() + public void WaitForStorageContractFinished(ICodexContracts contracts) { if (!contractStartedUtc.HasValue) { @@ -74,6 +76,12 @@ namespace CodexPlugin contractFinishedUtc = DateTime.UtcNow; LogFinishedDuration(); AssertDuration(SubmittedToFinished, timeout, nameof(SubmittedToFinished)); + + contracts.WaitUntilNextPeriod(); + + var blocks = 3; + Log($"Waiting {blocks} blocks for nodes to process payouts..."); + Thread.Sleep(GethContainerRecipe.BlockInterval * blocks); } public StoragePurchase GetPurchaseStatus(string purchaseId) diff --git a/ProjectPlugins/GethPlugin/GethContainerRecipe.cs b/ProjectPlugins/GethPlugin/GethContainerRecipe.cs index e4c2fe8..8fc5239 100644 --- a/ProjectPlugins/GethPlugin/GethContainerRecipe.cs +++ b/ProjectPlugins/GethPlugin/GethContainerRecipe.cs @@ -6,6 +6,7 @@ namespace GethPlugin public class GethContainerRecipe : ContainerRecipeFactory { public static string DockerImage { get; } = "codexstorage/dist-tests-geth:latest"; + public static TimeSpan BlockInterval { get; } = TimeSpan.FromSeconds(1.0); private const string defaultArgs = "--ipcdisable --syncmode full"; public const string HttpPortTag = "http_port"; diff --git a/Tests/CodexReleaseTests/MarketTests/ContractSuccessfulTest.cs b/Tests/CodexReleaseTests/MarketTests/ContractSuccessfulTest.cs index 71ad111..f375e11 100644 --- a/Tests/CodexReleaseTests/MarketTests/ContractSuccessfulTest.cs +++ b/Tests/CodexReleaseTests/MarketTests/ContractSuccessfulTest.cs @@ -1,6 +1,7 @@ using CodexContractsPlugin; using CodexPlugin; using GethPlugin; +using Nethereum.Hex.HexConvertors.Extensions; using NUnit.Framework; using Utils; @@ -15,7 +16,7 @@ namespace CodexReleaseTests.MarketTests protected override int NumberOfHosts => 4; protected override int NumberOfClients => 1; protected override ByteSize HostAvailabilitySize => (5 * FilesizeMb).MB(); - protected override TimeSpan HostAvailabilityMaxDuration => GetHostAvailabilityDuration(); + protected override TimeSpan HostAvailabilityMaxDuration => Get8TimesConfiguredPeriodDuration(); [Test] public void ContractSuccessful() @@ -29,26 +30,15 @@ namespace CodexReleaseTests.MarketTests AssertContractIsOnChain(request); request.WaitForStorageContractStarted(); - var slotFills = AssertContractSlotsAreFilledByHosts(request, hosts); + AssertContractSlotsAreFilledByHosts(request, hosts); - request.WaitForStorageContractFinished(); - //EveryoneWithdrawsFunds(hosts, client); + request.WaitForStorageContractFinished(GetContracts()); - GetContracts().WithdrawFunds(request.PurchaseId, client.EthAddress); - foreach (var host in hosts) - GetContracts().WithdrawFunds(request.PurchaseId, host.EthAddress); - - AssertClientHasPaidForContract(client, request); - AssertHostsWerePaidForContract(request, hosts, slotFills); + AssertClientHasPaidForContract(client, request, hosts); + AssertHostsWerePaidForContract(request, hosts); AssertHostsCollateralsAreUnchanged(hosts); } - //private void EveryoneWithdrawsFunds(ICodexNodeGroup hosts, ICodexNode client) - //{ - // foreach (var host in hosts) host.Marketplace.WithdrawFunds(); - // client.Marketplace.WithdrawFunds(); - //} - private void AssertContractIsOnChain(IStoragePurchaseContract contract) { AssertOnChainEvents(events => @@ -59,7 +49,7 @@ namespace CodexReleaseTests.MarketTests }, nameof(AssertContractIsOnChain)); } - private SlotFill[] AssertContractSlotsAreFilledByHosts(IStoragePurchaseContract contract, ICodexNodeGroup hosts) + private void AssertContractSlotsAreFilledByHosts(IStoragePurchaseContract contract, ICodexNodeGroup hosts) { var activeHosts = new Dictionary(); @@ -78,30 +68,32 @@ namespace CodexReleaseTests.MarketTests if (activeHosts.Count != contract.Purchase.MinRequiredNumberOfNodes) throw new Exception("Not all slots were filled..."); }, nameof(AssertContractSlotsAreFilledByHosts)); - - return activeHosts.Values.ToArray(); } - private void AssertClientHasPaidForContract(ICodexNode client, IStoragePurchaseContract contract) + private void AssertClientHasPaidForContract(ICodexNode client, IStoragePurchaseContract contract, ICodexNodeGroup hosts) { - var balance = GetContracts().GetTestTokenBalance(client); - var expectedBalance = StartingBalanceTST.Tst() - GetContractTotalCost(); + var balance = GetTstBalance(client); + var expectedBalance = StartingBalanceTST.Tst() - GetContractFinalCost(contract, hosts); Assert.That(balance, Is.EqualTo(expectedBalance), "Client balance incorrect."); } - private void AssertHostsWerePaidForContract(IStoragePurchaseContract contract, ICodexNodeGroup hosts, SlotFill[] fills) + private void AssertHostsWerePaidForContract(IStoragePurchaseContract contract, ICodexNodeGroup hosts) { + var fills = GetOnChainSlotFills(hosts); + var submitUtc = GetContractOnChainSubmittedUtc(contract); + var finishUtc = submitUtc + contract.Purchase.Duration; var expectedBalances = new Dictionary(); foreach (var host in hosts) expectedBalances.Add(host.EthAddress, StartingBalanceTST.Tst()); foreach (var fill in fills) { - expectedBalances[fill.Host.EthAddress] += GetContractCostPerSlot(); + var slotDuration = finishUtc - fill.SlotFilledEvent.Block.Utc; + expectedBalances[fill.Host.EthAddress] += GetContractCostPerSlot(slotDuration); } foreach (var pair in expectedBalances) { - var balance = GetContracts().GetTestTokenBalance(pair.Key); + var balance = GetTstBalance(pair.Key); Assert.That(balance, Is.EqualTo(pair.Value), "Host was not paid for storage."); } } @@ -112,7 +104,7 @@ namespace CodexReleaseTests.MarketTests // All host balances should be equal to or greater than the starting balance. foreach (var host in hosts) { - Assert.That(GetContracts().GetTestTokenBalance(host), Is.GreaterThanOrEqualTo(StartingBalanceTST.Tst())); + Assert.That(GetTstBalance(host), Is.GreaterThanOrEqualTo(StartingBalanceTST.Tst())); } } @@ -141,15 +133,32 @@ namespace CodexReleaseTests.MarketTests }); } - private TestToken GetContractTotalCost() + private TestToken GetContractFinalCost(IStoragePurchaseContract contract, ICodexNodeGroup hosts) { - return GetContractCostPerSlot() * NumberOfHosts; + var fills = GetOnChainSlotFills(hosts); + var result = 0.Tst(); + var submitUtc = GetContractOnChainSubmittedUtc(contract); + var finishUtc = submitUtc + contract.Purchase.Duration; + + foreach (var fill in fills) + { + var slotDuration = finishUtc - fill.SlotFilledEvent.Block.Utc; + result += GetContractCostPerSlot(slotDuration); + } + + return result; } - private TestToken GetContractCostPerSlot() + private DateTime GetContractOnChainSubmittedUtc(IStoragePurchaseContract contract) { - var duration = GetContractDuration(); - return PricePerSlotPerSecondTSTWei.TstWei() * ((int)duration.TotalSeconds); + var events = GetContracts().GetEvents(GetTestRunTimeRange()); + var submitEvent = events.GetStorageRequests().Single(e => e.RequestId.ToHex(false) == contract.PurchaseId); + return submitEvent.Block.Utc; + } + + private TestToken GetContractCostPerSlot(TimeSpan slotDuration) + { + return PricePerSlotPerSecondTSTWei.TstWei() * (int)slotDuration.TotalSeconds; } private TimeSpan GetContractExpiry() @@ -159,10 +168,10 @@ namespace CodexReleaseTests.MarketTests private TimeSpan GetContractDuration() { - return GetHostAvailabilityDuration() / 2; + return Get8TimesConfiguredPeriodDuration() / 2; } - private TimeSpan GetHostAvailabilityDuration() + private TimeSpan Get8TimesConfiguredPeriodDuration() { var config = GetContracts().Deployment.Config; return TimeSpan.FromSeconds(((double)config.Proofs.Period) * 8.0); diff --git a/Tests/CodexReleaseTests/MarketTests/MarketplaceAutoBootstrapDistTest.cs b/Tests/CodexReleaseTests/MarketTests/MarketplaceAutoBootstrapDistTest.cs index b708796..89284b7 100644 --- a/Tests/CodexReleaseTests/MarketTests/MarketplaceAutoBootstrapDistTest.cs +++ b/Tests/CodexReleaseTests/MarketTests/MarketplaceAutoBootstrapDistTest.cs @@ -5,6 +5,7 @@ using CodexTests; using DistTestCore; using GethPlugin; using Nethereum.Hex.HexConvertors.Extensions; +using NUnit.Framework; using Utils; namespace CodexReleaseTests.MarketTests @@ -13,6 +14,7 @@ namespace CodexReleaseTests.MarketTests { private readonly Dictionary handles = new Dictionary(); protected const int StartingBalanceTST = 1000; + protected const int StartingBalanceEth = 10; protected override void LifecycleStart(TestLifecycle lifecycle) { @@ -48,7 +50,7 @@ namespace CodexReleaseTests.MarketTests var hosts = StartCodex(NumberOfHosts, s => s .WithName("host") .EnableMarketplace(GetGeth(), GetContracts(), m => m - .WithInitial(10.Eth(), StartingBalanceTST.Tst()) + .WithInitial(StartingBalanceEth.Eth(), StartingBalanceTST.Tst()) .AsStorageNode() ) ); @@ -56,6 +58,9 @@ namespace CodexReleaseTests.MarketTests var config = GetContracts().Deployment.Config; foreach (var host in hosts) { + Assert.That(GetTstBalance(host).TstWei, Is.EqualTo(StartingBalanceTST.Tst().TstWei)); + Assert.That(GetEthBalance(host).Wei, Is.EqualTo(StartingBalanceEth.Eth().Wei)); + host.Marketplace.MakeStorageAvailable(new CodexPlugin.StorageAvailability( totalSpace: HostAvailabilitySize, maxDuration: HostAvailabilityMaxDuration, @@ -66,12 +71,32 @@ namespace CodexReleaseTests.MarketTests return hosts; } + public TestToken GetTstBalance(ICodexNode node) + { + return GetContracts().GetTestTokenBalance(node); + } + + public TestToken GetTstBalance(EthAddress address) + { + return GetContracts().GetTestTokenBalance(address); + } + + public Ether GetEthBalance(ICodexNode node) + { + return GetGeth().GetEthBalance(node); + } + + public Ether GetEthBalance(EthAddress address) + { + return GetGeth().GetEthBalance(address); + } + public ICodexNodeGroup StartClients() { return StartCodex(NumberOfClients, s => s .WithName("client") .EnableMarketplace(GetGeth(), GetContracts(), m => m - .WithInitial(10.Eth(), StartingBalanceTST.Tst()) + .WithInitial(StartingBalanceEth.Eth(), StartingBalanceTST.Tst()) ) ); } diff --git a/Tests/CodexTests/BasicTests/MarketplaceTests.cs b/Tests/CodexTests/BasicTests/MarketplaceTests.cs index 9bb0f6f..d8e178f 100644 --- a/Tests/CodexTests/BasicTests/MarketplaceTests.cs +++ b/Tests/CodexTests/BasicTests/MarketplaceTests.cs @@ -107,7 +107,7 @@ namespace CodexTests.BasicTests AssertStorageRequest(request, purchase, contracts, client); AssertContractSlot(contracts, request, 0); - purchaseContract.WaitForStorageContractFinished(); + purchaseContract.WaitForStorageContractFinished(contracts); AssertBalance(contracts, client, Is.LessThan(clientInitialBalance), "Buyer was not charged for storage."); Assert.That(contracts.GetRequestState(request), Is.EqualTo(RequestState.Finished)); diff --git a/Tests/CodexTests/UtilityTests/DiscordBotTests.cs b/Tests/CodexTests/UtilityTests/DiscordBotTests.cs index 50094dc..b22fcba 100644 --- a/Tests/CodexTests/UtilityTests/DiscordBotTests.cs +++ b/Tests/CodexTests/UtilityTests/DiscordBotTests.cs @@ -45,7 +45,7 @@ namespace CodexTests.UtilityTests var purchaseContract = ClientPurchasesStorage(client); purchaseContract.WaitForStorageContractStarted(); - purchaseContract.WaitForStorageContractFinished(); + purchaseContract.WaitForStorageContractFinished(contracts); Thread.Sleep(rewarderInterval * 3); apiCalls.Stop();