public async Task payment_claim_should_not_be_sent_while_units_are_less_than_units_range_to() { var unitsRange = new UnitsRange(0, 5); var paymentClaim = new PaymentClaim(id: Keccak.Zero, depositId: Keccak.Zero, assetId: Keccak.Zero, assetName: "test", units: 3, claimedUnits: 3, unitsRange: unitsRange, value: 10, claimedValue: 30, expiryTime: 10, pepper: Array.Empty <byte>(), provider: TestItem.AddressA, consumer: TestItem.AddressB, signature: new Signature(1, 2, 37), timestamp: 12, transactions: Array.Empty <TransactionInfo>(), status: PaymentClaimStatus.Unknown ); Keccak?transactionHash = await _processor.SendTransactionAsync(paymentClaim, 10); Assert.IsNull(transactionHash); Assert.IsTrue(paymentClaim.Status != PaymentClaimStatus.Sent); }
private DataDeliveryReceiptRequest CreateRequest(Keccak depositId, Keccak sessionId, uint unpaidSessionUnits, bool isSettlement = false) { if (_logger.IsInfo) { _logger.Info($"Creating a receipt request for deposit: '{depositId}', session: '{sessionId}'."); } IDepositNodesHandler?deposit = GetDepositNodesHandler(depositId); if (deposit == null) { throw new InvalidDataException($"Cannot resolve deposit for deposit ID {depositId}"); } uint number = deposit.GetNextReceiptRequestNumber(); uint latestReceiptRangeTo = deposit.LatestReceipt?.Request.UnitsRange.To ?? 0; uint rangeFrom = deposit.LatestReceipt is null ? 0 : latestReceiptRangeTo + 1; uint rangeTo = rangeFrom + unpaidSessionUnits - 1; rangeTo = rangeTo >= deposit.PurchasedUnits ? (uint)deposit.PurchasedUnits - 1 : rangeTo; UnitsRange unitsRange = new UnitsRange(rangeFrom, rangeTo); if (_logger.IsInfo) { _logger.Info($"Created a receipt request for deposit: '{depositId}', session: '{sessionId}', range: [{unitsRange.From}, {unitsRange.To}]."); } return(new DataDeliveryReceiptRequest(number, depositId, unitsRange, isSettlement)); }
public DataDeliveryReceiptToMerge Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { rlpStream.ReadSequenceLength(); UnitsRange unitsRange = Serialization.Rlp.Rlp.Decode <UnitsRange>(rlpStream); Signature signature = SignatureDecoder.DecodeSignature(rlpStream); return(new DataDeliveryReceiptToMerge(unitsRange, signature)); }
public DataDeliveryReceiptRequest Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { rlpStream.ReadSequenceLength(); uint number = rlpStream.DecodeUInt(); Keccak depositId = rlpStream.DecodeKeccak(); UnitsRange unitsRange = Serialization.Rlp.Rlp.Decode <UnitsRange>(rlpStream); bool isSettlement = rlpStream.DecodeBool(); var receipts = Serialization.Rlp.Rlp.DecodeArray <DataDeliveryReceiptToMerge>(rlpStream); return(new DataDeliveryReceiptRequest(number, depositId, unitsRange, isSettlement, receipts)); }
public async Task given_null_transaction_hash_returned_payment_claim_should_have_status_unknown() { var unitsRange = new UnitsRange(0, 1); var consumer = CreateConsumer(); _consumerRepository.GetAsync(Arg.Any <Keccak>()).Returns(consumer); var receiptRequest = new DataDeliveryReceiptRequest(1, Keccak.Zero, unitsRange); var paymentClaim = await _processor.ProcessAsync(receiptRequest, null); paymentClaim.Transaction.Should().BeNull(); paymentClaim.Status.Should().Be(PaymentClaimStatus.Unknown); }
public void given_units_range_from_5_to_9_and_6_unpaid_units_request_should_be_valid() { var unpaidUnits = 6; var consumedUnits = 0; var purchasedUnits = 100; var unitsRange = new UnitsRange(5, 9); var receiptRequest = CreateRequest(unitsRange); var isValid = _validator.IsValid(receiptRequest, unpaidUnits, consumedUnits, purchasedUnits); isValid.Should().BeTrue(); }
public void given_previous_range_from_0_to_9_and_actual_range_from_10_to_19_and_10_unpaid_units_request_should_be_valid() { var unpaidUnits = 10; var consumedUnits = 0; var purchasedUnits = 100; var previousUnitsRange = new UnitsRange(0, 9); var unitsRange = new UnitsRange(10, 19); var previousReceipt = new DataDeliveryReceiptToMerge(previousUnitsRange, null); var receiptRequest = CreateRequest(unitsRange, new[] { previousReceipt }); var isValid = _validator.IsValid(receiptRequest, unpaidUnits, consumedUnits, purchasedUnits); isValid.Should().BeTrue(); }
public async Task given_receipt_request_range_0_to_19_payment_claim_range_should_be_0_to_19() { var unitsRange = new UnitsRange(0, 19); var consumer = CreateConsumer(); _consumerRepository.GetAsync(Arg.Any <Keccak>()).Returns(consumer); _paymentService.ClaimPaymentAsync(Arg.Any <PaymentClaim>(), _coldWalletAddress, _gasPrice) .Returns(_transaction.Hash); var receiptRequest = new DataDeliveryReceiptRequest(1, Keccak.Zero, unitsRange); var paymentClaim = await _processor.ProcessAsync(receiptRequest, null); paymentClaim.ClaimedUnits.Should().Be(20); paymentClaim.UnitsRange.Should().Be(unitsRange); }
public async Task given_valid_transaction_hash_returned_payment_claim_should_have_status_sent() { var unitsRange = new UnitsRange(0, 1); var consumer = CreateConsumer(); _consumerRepository.GetAsync(Arg.Any <Keccak>()).Returns(consumer); _paymentService.ClaimPaymentAsync(Arg.Any <PaymentClaim>(), _coldWalletAddress, _gasPrice) .Returns(_transaction.Hash); var receiptRequest = new DataDeliveryReceiptRequest(1, Keccak.Zero, unitsRange); var paymentClaim = await _processor.ProcessAsync(receiptRequest, null); paymentClaim.Transaction.Hash.Should().Be(_transaction.Hash); paymentClaim.Status.Should().Be(PaymentClaimStatus.Sent); }
public async Task given_invalid_consumer_address_delivery_receipt_should_not_be_processed() { _signer.RecoverPublicKey(Arg.Any <Signature>(), Arg.Any <Keccak>()).ReturnsForAnyArgs(TestItem.PublicKeyB); _peer.ConsumerAddress.Returns(TestItem.AddressC); var currentReceiptRequestUnitsRange = new UnitsRange(0, 9); var receiptRequest = new DataDeliveryReceiptRequest(1, Keccak.Zero, currentReceiptRequestUnitsRange); var deliveryReceipt = new DataDeliveryReceipt(StatusCodes.Ok, 0, 0, new Signature(new byte[65])); var wasProcessed = await _processor.TryProcessAsync(_session, _publicKey.Address, _peer, receiptRequest, deliveryReceipt); wasProcessed.Should().BeFalse(); _session.DataAvailability.Should().Be(DataAvailability.DataDeliveryReceiptInvalid); }
public void given_previous_ranges_from_0_to_11_and_actual_range_from_0_to_22_and_11_unpaid_units_request_should_be_valid() { var unpaidUnits = 11; var consumedUnits = 0; var purchasedUnits = 100; var unitsRange = new UnitsRange(0, 22); var previousReceipts = new[] { new DataDeliveryReceiptToMerge(new UnitsRange(0, 11), null) }; var receiptRequest = CreateRequest(unitsRange, previousReceipts); var isValid = _validator.IsValid(receiptRequest, unpaidUnits, consumedUnits, purchasedUnits); isValid.Should().BeTrue(); }
private async Task <TxReceipt> ClaimPaymentFor(DataRequest dataRequest, Keccak depositId, uint start, uint end, Address payToAccount) { UInt256 payToBalanceBefore = _state.GetBalance(payToAccount); UInt256 providerBalanceBefore = _state.GetBalance(_providerAccount); UInt256 feeBalanceBefore = _state.GetBalance(_feeAccount); UnitsRange unitRange = new UnitsRange(start, end); UInt256 value = dataRequest.Units * (UInt256)dataRequest.Value / dataRequest.Units; uint claimedUnits = unitRange.Units; UInt256 claimedValue = claimedUnits * (UInt256)dataRequest.Value / dataRequest.Units; byte[] receiptData = _abiEncoder.Encode(AbiEncodingStyle.Packed, _receiptAbiDef, depositId.Bytes, unitRange.From, unitRange.To); Signature receiptSig = _wallet.Sign(Keccak.Compute(receiptData), _consumerAccount); PaymentClaim paymentClaim = new PaymentClaim(Keccak.Zero, depositId, dataRequest.DataAssetId, "data asset", dataRequest.Units, claimedUnits, unitRange, value, (UInt256)claimedValue, dataRequest.ExpiryTime, dataRequest.Pepper, _providerAccount, _consumerAccount, receiptSig, 0, Array.Empty <TransactionInfo>(), PaymentClaimStatus.Unknown); UInt256 gasPrice = 20.GWei(); Keccak paymentTxHash = await _paymentService.ClaimPaymentAsync(paymentClaim, payToAccount, gasPrice); _bridge.IncrementNonce(_providerAccount); UInt256 feeBalanceAfter = _state.GetBalance(_feeAccount); UInt256 providerBalanceAfter = _state.GetBalance(_providerAccount); UInt256 payToBalanceAfter = _state.GetBalance(payToAccount); TxReceipt txReceipt = _bridge.GetReceipt(paymentTxHash); TestContext.WriteLine("GAS USED FOR PAYMENT CLAIM: {0}", txReceipt.GasUsed); TestContext.WriteLine($"(FEE) BALANCE BEFORE: {feeBalanceBefore}"); TestContext.WriteLine($"(FEE) DIFFERENCE: {feeBalanceAfter - feeBalanceBefore}"); TestContext.WriteLine($"(PAY TO) BALANCE BEFORE: {payToBalanceBefore}"); TestContext.WriteLine($"(PAY TO) DIFFERENCE: {payToBalanceAfter - payToBalanceBefore}"); TestContext.WriteLine($"(PROVIDER) BALANCE BEFORE: {providerBalanceBefore}"); TestContext.WriteLine($"(PROVIDER) DIFFERENCE: {(BigInteger) providerBalanceAfter - (BigInteger) providerBalanceBefore}"); bool isProviderSameAsPayTo = _providerAccount.Equals(payToAccount); UInt256 expectedPayment = (8 * dataRequest.Value * (unitRange.To - unitRange.From + 1)) / (dataRequest.Units * 10); UInt256 expectedGasFee = (UInt256)txReceipt.GasUsed * 20.GWei(); UInt256 expectedFee = 2 * dataRequest.Value * unitRange.Units / (dataRequest.Units * 10); Assert.AreEqual((UInt256)20, 100 * expectedFee / (expectedPayment + expectedFee), "fee %"); Assert.AreEqual(expectedFee, feeBalanceAfter - feeBalanceBefore, "fee"); Assert.AreEqual(providerBalanceBefore - expectedGasFee + (isProviderSameAsPayTo ? UInt256.One : UInt256.Zero) * expectedPayment, providerBalanceAfter, $"before: {providerBalanceBefore}, after: {providerBalanceAfter}"); Assert.AreEqual(payToBalanceBefore - (isProviderSameAsPayTo ? UInt256.One : UInt256.Zero) * expectedGasFee + expectedPayment, payToBalanceAfter, $"before: {payToBalanceBefore}, after: {payToBalanceAfter}"); return(txReceipt); }
public async Task <Keccak?> SendTransactionAsync(PaymentClaim paymentClaim, UInt256 gasPrice) { bool isPaymentClaimCorrect = IsPaymentClaimCorrect(paymentClaim); if (!isPaymentClaimCorrect) { if (_logger.IsWarn) { _logger.Warn($"Payment claim id: {paymentClaim.Id} was incorrect. Claim will be rejected"); } paymentClaim.Reject(); await _paymentClaimRepository.UpdateAsync(paymentClaim); return(null); } Keccak depositId = paymentClaim.DepositId; UnitsRange range = paymentClaim.UnitsRange; Keccak? transactionHash = await _paymentService.ClaimPaymentAsync(paymentClaim, _coldWalletAddress, gasPrice); bool isTransactionHashValid = !(transactionHash is null) && transactionHash != Keccak.Zero; if (isTransactionHashValid) { if (_logger.IsInfo) { _logger.Info($"Received a transaction hash: {transactionHash} for payment claim (id: '{paymentClaim.Id}') for deposit: '{depositId}', range: [{range.From}, {range.To}], units: {paymentClaim.Units}."); } return(transactionHash); } if (_logger.IsError) { _logger.Error($"There was an error when claiming a payment (id: '{paymentClaim.Id}') for deposit: '{depositId}', range: [{range.From}, {range.To}] with receipt [{range.From}, {range.To}], units: {paymentClaim.Units} - returned transaction is empty."); } return(null); }
private DataDeliveryReceiptRequest?CreateMergedRequest(IDepositNodesHandler deposit) { if (deposit.LatestReceipt == null) { return(null); } if (_logger.IsInfo) { _logger.Info($"Creating merged receipt request for deposit: '{deposit.DepositId}'."); } bool isLastReceipt = deposit.LatestReceipt.Request.UnitsRange.To == deposit.PurchasedUnits - 1; if (isLastReceipt && _logger.IsInfo) { _logger.Info($"Merged receipt request for deposit: '{deposit.DepositId}' will be the last one."); } uint latestPaymentClaimRangeTo = deposit.LatestPaymentClaim?.UnitsRange.To ?? 0; DataDeliveryReceiptDetails?latestMergedReceipt = deposit.Receipts.OrderBy(r => r.Timestamp) .ThenBy(r => r.Request.UnitsRange.To) .LastOrDefault(r => r.IsMerged); uint latestMergedReceiptRangeFrom = latestMergedReceipt?.Request.UnitsRange.From ?? 0; long latestMergedReceiptRangeTo = latestMergedReceipt?.Request.UnitsRange.To ?? 0; var mergeableReceipts = deposit.Receipts .Where(r => r.Timestamp >= (latestMergedReceipt?.Timestamp ?? 0) && r.Request.UnitsRange.To > (latestMergedReceipt is null ? -1 : latestMergedReceiptRangeTo)) .OrderBy(r => r.Timestamp) .ThenBy(r => r.Request.UnitsRange.To) .ToList(); var paymentClaimExist = deposit.LatestPaymentClaim is { }; uint rangeFrom; if (paymentClaimExist) { rangeFrom = latestPaymentClaimRangeTo + 1; } else { if (latestMergedReceiptRangeFrom > 0) { rangeFrom = latestMergedReceiptRangeFrom + 1; } else { rangeFrom = 0; } } uint rangeTo = deposit.LatestReceipt.Request.UnitsRange.To; if (rangeFrom > rangeTo) { return(null); } if (!(latestMergedReceipt is null)) { mergeableReceipts.Insert(0, latestMergedReceipt); } UnitsRange unitsRange = new UnitsRange(rangeFrom, rangeTo); var receiptsToMerge = mergeableReceipts.Select(r => new DataDeliveryReceiptToMerge( r.Request.UnitsRange, r.Receipt.Signature)).OrderBy(r => r.UnitsRange.To).ToList(); uint number = deposit.GetNextReceiptRequestNumber(); if (_logger.IsInfo) { _logger.Info($"Created merged receipt request for deposit: '{deposit.DepositId}' [{rangeFrom}, {rangeTo}]."); } return(new DataDeliveryReceiptRequest(number, deposit.DepositId, unitsRange, receiptsToMerge: receiptsToMerge)); }
private async Task <DataDeliveryReceiptDetails?> TryHandleReceiptAsync(ProviderSession session, Address consumer, INdmProviderPeer peer, DataDeliveryReceiptRequest request) { Keccak depositId = session.DepositId; IDepositNodesHandler?deposit = GetDepositNodesHandler(depositId); if (deposit == null) { throw new InvalidDataException($"Cannot resolve deposit handle for deposit ID: {depositId}"); } try { UnitsRange unitsRange = request.UnitsRange; if (_logger.IsInfo) { _logger.Info($"Sending data delivery receipt request ({request.Number}), deposit: '{depositId}', session: '{session.Id}', range: [{unitsRange.From}, {unitsRange.To}]."); } if (request.ReceiptsToMerge.Any()) { if (_logger.IsInfo) { _logger.Info($"Receipts to merge for deposit: '{depositId}': {string.Join(", ", request.ReceiptsToMerge.Select(r => $"[{r.UnitsRange.From}, {r.UnitsRange.To}]"))}"); } } (DataDeliveryReceipt receipt, RequestReceiptStatus status) = await TryHandleDeliveryReceipt(deposit, consumer, session, request, peer); Keccak id = Keccak.Compute(Rlp.Encode(Rlp.Encode(depositId), Rlp.Encode(request.Number)).Bytes); DataDeliveryReceiptDetails receiptDetails = new DataDeliveryReceiptDetails(id, session.Id, session.DataAssetId, peer.NodeId, request, receipt, _timestamper.UnixTime.Seconds, false); await _receiptRepository.AddAsync(receiptDetails); if (status != RequestReceiptStatus.Ok) { return(null); } UnitsRange range = request.UnitsRange; uint claimedUnits = range.To - range.From + 1; deposit.AddReceipt(receiptDetails); if (!request.ReceiptsToMerge.Any()) { deposit.SubtractUnpaidUnits(claimedUnits); } if (_logger.IsInfo) { _logger.Info($"Successfully processed receipt ({claimedUnits} units) for deposit: '{depositId}' with node: '{peer.NodeId}'."); } return(receiptDetails); } catch (Exception ex) { if (_logger.IsWarn) { _logger.Warn($"There was an error when processing a receipt for deposit: '{depositId}'. {ex}"); } session.SetDataAvailability(DataAvailability.DataDeliveryReceiptNotProvided); await _sessionRepository.UpdateAsync(session); peer.SendDataAvailability(depositId, DataAvailability.DataDeliveryReceiptInvalid); return(null); } }
private async Task <(DataDeliveryReceipt, RequestReceiptStatus)> TryHandleDeliveryReceipt(IDepositNodesHandler deposit, Address consumer, ProviderSession session, DataDeliveryReceiptRequest request, INdmProviderPeer peer, bool isRetry = false) { while (true) { Keccak depositId = session.DepositId; DataDeliveryReceipt receipt = await peer.SendRequestDataDeliveryReceiptAsync(request, CancellationToken.None); if (_logger.IsInfo) { _logger.Info($"Received a delivery receipt for deposit: '{depositId}' from node: '{peer.NodeId}', status code: '{receipt.StatusCode}'."); } if (isRetry && receipt.StatusCode != StatusCodes.Ok) { if (_logger.IsWarn) { _logger.Warn($"Request receipt retry failed for: '{depositId}' from node: '{peer.NodeId}', status code: '{receipt.StatusCode}'."); } return(receipt, RequestReceiptStatus.Invalid); } switch (receipt.StatusCode) { case StatusCodes.Ok: if (await _receiptProcessor.TryProcessAsync(session, consumer, peer, request, receipt)) { return(receipt, RequestReceiptStatus.Ok); } return(receipt, RequestReceiptStatus.Invalid); case StatusCodes.InvalidReceiptRequestRange: { if (_logger.IsInfo) { _logger.Info($"Consumer for deposit: '{depositId}' from node: '{peer.NodeId}' claims {receipt.ConsumedUnits} consumed and {receipt.UnpaidUnits} unpaid units."); } UnitsRange range = request.UnitsRange; uint requestedUnits = range.To - range.From + 1; if (requestedUnits <= receipt.UnpaidUnits) { if (_logger.IsWarn) { _logger.Warn($"Consumer for deposit: '{depositId}' from node: '{peer.NodeId}' claimed an invalid range (while it was actually valid)."); } break; } uint graceUnits = requestedUnits - receipt.UnpaidUnits; uint totalGraceUnits = graceUnits + deposit.GraceUnits; bool hasReachedThreshold = await _receiptsPolicies.CanClaimPayment(totalGraceUnits, deposit.UnitPrice); if (hasReachedThreshold) { peer.SendGraceUnitsExceeded(depositId, deposit.ConsumedUnits, totalGraceUnits); if (_logger.IsWarn) { _logger.Warn($"Consumer for deposit: '{depositId}' from node: '{peer.NodeId}' claimed too many unpaid units, grace units exceeded ({totalGraceUnits})."); } return(receipt, RequestReceiptStatus.GraceUnitsExceeded); } if (_logger.IsInfo) { _logger.Info($"Unpaid units difference is: {graceUnits} ({requestedUnits} - {receipt.UnpaidUnits}). Lowering units amount for deposit: '{depositId}'."); } deposit.SubtractUnpaidUnits(graceUnits); deposit.SubtractUnmergedUnits(graceUnits); deposit.SubtractUnclaimedUnits(graceUnits); deposit.AddGraceUnits(graceUnits); session.SubtractUnpaidUnits(graceUnits); session.AddGraceUnits(graceUnits); if (range.To < graceUnits) { peer.SendGraceUnitsExceeded(depositId, deposit.ConsumedUnits, totalGraceUnits); if (_logger.IsWarn) { _logger.Warn($"Cannot request a receipt for deposit: '{depositId}' - grace units amount is greater than the receipt range ({graceUnits} > {range.To})."); } return(receipt, RequestReceiptStatus.GraceUnitsExceeded); } uint updatedRangeTo = range.To - graceUnits; if (range.From > updatedRangeTo) { if (_logger.IsWarn) { _logger.Warn($"Invalid updated range [{range.From}, {updatedRangeTo}] for: '{depositId}' - receipt request will not be send."); } return(receipt, RequestReceiptStatus.Invalid); } if (_logger.IsInfo) { _logger.Info($"Grace units for deposit: '{depositId}' is: {deposit.GraceUnits} (added {graceUnits})."); } UnitsRange updatedRange = new UnitsRange(range.From, updatedRangeTo); if (_logger.IsInfo) { _logger.Info($"Updated range for deposit: '{depositId}' [{updatedRange.From}, {updatedRange.To}]. Requesting receipt once again."); } request = request.WithRange(updatedRange, deposit.GetNextReceiptRequestNumber()); isRetry = true; continue; } case StatusCodes.InvalidReceiptAddress: { break; } case StatusCodes.Error: { break; } } return(receipt, RequestReceiptStatus.Invalid); } }
public async Task <PaymentClaim?> ProcessAsync(DataDeliveryReceiptRequest receiptRequest, Signature signature) { Keccak depositId = receiptRequest.DepositId; Consumer?consumer = await _consumerRepository.GetAsync(depositId); if (consumer is null) { if (_logger.IsError) { _logger.Error($"Could not find any consumers for deposit {depositId} in the repository."); } return(null); } DataRequest dataRequest = consumer.DataRequest; UnitsRange range = receiptRequest.UnitsRange; uint claimedUnits = range.Units; BigInteger claimedValue = claimedUnits * (BigInteger)consumer.DataRequest.Value / consumer.DataRequest.Units; ulong timestamp = _timestamper.UnixTime.Seconds; Rlp unitsRangeRlp = _unitsRangeRlpDecoder.Encode(range); Keccak id = Keccak.Compute(Rlp.Encode(Rlp.Encode(depositId), Rlp.Encode(timestamp), unitsRangeRlp).Bytes); PaymentClaim paymentClaim = new PaymentClaim(id, consumer.DepositId, consumer.DataAsset.Id, consumer.DataAsset.Name, dataRequest.Units, claimedUnits, range, dataRequest.Value, (UInt256)claimedValue, dataRequest.ExpiryTime, dataRequest.Pepper, dataRequest.Provider, dataRequest.Consumer, signature, timestamp, Array.Empty <TransactionInfo>(), PaymentClaimStatus.Unknown); await _paymentClaimRepository.AddAsync(paymentClaim); if (_logger.IsInfo) { _logger.Info($"Claiming a payment (id: '{paymentClaim.Id}') for deposit: '{depositId}', range: [{range.From}, {range.To}], units: {claimedUnits}."); } UInt256 gasPrice = await _gasPriceService.GetCurrentPaymentClaimGasPriceAsync(); Keccak?transactionHash = null; if (_disableSendingPaymentClaimTransaction) { if (_logger.IsWarn) { _logger.Warn("*** NDM provider sending payment claim transaction is disabled ***"); } } else { transactionHash = await SendTransactionAsync(paymentClaim, gasPrice); if (transactionHash is null) { if (_logger.IsInfo) { _logger.Info($"Payment claim (id: {paymentClaim.Id}) for deposit: '{paymentClaim.DepositId}' did not receive a transaction hash."); } return(paymentClaim); } } if (_logger.IsInfo) { _logger.Info($"Payment claim (id: {paymentClaim.Id}) for deposit: '{paymentClaim.DepositId}' received a transaction hash: '{transactionHash}'."); } paymentClaim.AddTransaction(TransactionInfo.Default(transactionHash, 0, gasPrice, _paymentService.GasLimit, timestamp)); paymentClaim.SetStatus(PaymentClaimStatus.Sent); await _paymentClaimRepository.UpdateAsync(paymentClaim); string claimedEth = ((decimal)claimedValue / Eth).ToString("0.0000"); if (_logger.IsInfo) { _logger.Info($"Sent a payment claim (id: '{paymentClaim.Id}') for deposit: '{depositId}', range: [{range.From}, {range.To}], units: {claimedUnits}, units: {claimedUnits}, value: {claimedValue} wei ({claimedEth} ETH, transaction hash: '{transactionHash}'."); } return(paymentClaim); }
public UnitsRangeForRpc(UnitsRange range) { From = range.From; To = range.To; }
private async Task TryMergeReceiptsAsync(IDepositNodesHandler deposit) { Keccak depositId = deposit.DepositId; if (deposit.HasSentLastMergedReceipt) { if (_logger.IsInfo) { _logger.Info($"Last receipt request for deposit: '{depositId}' was already sent."); } return; } if (deposit.HasClaimedAllUnits) { if (_logger.IsInfo) { _logger.Info($"Last receipt request for deposit: '{depositId}' was already claimed."); } return; } DataDeliveryReceiptDetails?latestReceipt = deposit.LatestMergedReceipt; if (latestReceipt?.Request.UnitsRange.To == deposit.PurchasedUnits) { if (_logger.IsInfo) { _logger.Info($"Last receipt request for deposit: '{depositId}' was already merged."); } return; } var consumerNodes = _sessionManager.GetConsumerNodes(depositId); foreach (ConsumerNode node in consumerNodes) { if (_logger.IsInfo) { _logger.Info($"Trying to merge receipts for deposit: '{depositId}' with node: '{node.Peer.NodeId}'."); } try { ProviderSession?session = node.GetSession(depositId); if (session is null) { if (_logger.IsInfo) { _logger.Info($"Session was not found for deposit: '{depositId}', node: '{node.Peer.NodeId}'."); } continue; } DataDeliveryReceiptRequest?request = CreateMergedRequest(deposit); if (request is null) { if (_logger.IsInfo) { _logger.Info($"Merged receipt for deposit: '{depositId}' couldn't be created."); } return; } if (latestReceipt?.Request.UnitsRange.Equals(request.UnitsRange) == true) { if (_logger.IsInfo) { _logger.Info($"Merged receipt request for deposit: '{depositId}' would be the same as a previous one (already sent)."); } continue; } uint previouslyMergedUnits = latestReceipt?.Request.UnitsRange.To + 1 ?? 0; DataDeliveryReceiptDetails?receiptDetails = await TryHandleReceiptAsync(session, deposit.Consumer, node.Peer, request); if (receiptDetails is null) { if (_logger.IsWarn) { _logger.Warn($"Couldn't merge receipts for deposit: '{depositId}' with node: '{node.Peer.NodeId}'."); } continue; } deposit.AddReceipt(receiptDetails); UnitsRange range = receiptDetails.Request.UnitsRange; uint mergedTo = range.To + 1; if (mergedTo <= previouslyMergedUnits) { return; } uint mergedUnits = mergedTo - previouslyMergedUnits; mergedUnits = deposit.UnmergedUnits < mergedUnits ? deposit.UnmergedUnits : mergedUnits; deposit.SubtractUnmergedUnits(mergedUnits); if (_logger.IsInfo) { _logger.Info($"Successfully merged receipts ({mergedUnits} units) for deposit: '{depositId}' with node: '{node.Peer.NodeId}'."); } break; } catch (Exception ex) { if (_logger.IsWarn) { _logger.Warn($"There was an error when merging receipt requests for deposit: '{deposit.DepositId}'. {ex}"); } } } }
private static DataDeliveryReceiptRequest CreateRequest(UnitsRange unitsRange, IEnumerable <DataDeliveryReceiptToMerge> previousReceipts = null) => new DataDeliveryReceiptRequest(1, Keccak.Zero, unitsRange, false, previousReceipts ?? Enumerable.Empty <DataDeliveryReceiptToMerge>());