/// <inheritdoc/> public async Task <FeeProposalPayload> MultiSigMemberProposedInteropFeeAsync(string requestId, ulong feeAmount, PubKey pubKey) { using (await this.asyncLockObject.LockAsync().ConfigureAwait(false)) { // If this node does not have the fee proposal, return and wait for the matured blocks sync manager to find and create it. InteropConversionRequestFee interopConversionRequestFee = GetInteropConversionRequestFeeLocked(requestId); if (interopConversionRequestFee == null) { return(null); } // Check if the incoming proposal has not yet concluded and that the node has not yet proposed it. if (!HasFeeProposalBeenConcluded(interopConversionRequestFee) && !interopConversionRequestFee.FeeProposals.Any(p => p.PubKey == pubKey.ToHex())) { // Check that the fee proposal is in range. if (!IsFeeWithinAcceptableRange(interopConversionRequestFee.FeeProposals, requestId, feeAmount, pubKey)) { return(null); } interopConversionRequestFee.FeeProposals.Add(new InterOpFeeToMultisig() { BlockHeight = interopConversionRequestFee.BlockHeight, PubKey = pubKey.ToHex(), FeeAmount = feeAmount }); this.interopRequestKeyValueStore.SaveValueJson(interopConversionRequestFee.RequestId, interopConversionRequestFee, true); this.logger.LogDebug($"Received conversion request proposal '{requestId}' from '{pubKey}', proposing fee of {new Money(feeAmount)}."); } // This node would have proposed this fee if the InteropConversionRequestFee object exists. InterOpFeeToMultisig myProposal = interopConversionRequestFee.FeeProposals.First(p => p.PubKey == this.federationManager.CurrentFederationKey.PubKey.ToHex()); string signature = this.federationManager.CurrentFederationKey.SignMessage(interopConversionRequestFee.RequestId + myProposal.FeeAmount); return(FeeProposalPayload.Reply(interopConversionRequestFee.RequestId, myProposal.FeeAmount, interopConversionRequestFee.BlockHeight, signature)); } }
private async Task ProcessFeeProposalAsync(FeeProposalPayload payload) { if (!this.federationManager.IsFederationMember) { return; } // Check that the payload is signed by a multisig federation member. PubKey pubKey; try { pubKey = PubKey.RecoverFromMessage(payload.RequestId + payload.FeeAmount, payload.Signature); if (!this.federationManager.IsMultisigMember(pubKey)) { this.logger.LogWarning("Received unverified fee proposal payload for '{0}' from pubkey '{1}'.", payload.RequestId, pubKey?.ToHex()); return; } } catch (Exception) { this.logger.LogWarning("Received malformed fee proposal payload for '{0}'.", payload.RequestId); return; } // Reply back to the peer with this node's proposal. FeeProposalPayload replyToPayload = await this.conversionRequestFeeService.MultiSigMemberProposedInteropFeeAsync(payload.RequestId, payload.FeeAmount, pubKey).ConfigureAwait(false); if (payload.IsRequesting && replyToPayload != null) { await this.AttachedPeer.SendMessageAsync(replyToPayload).ConfigureAwait(false); } }
/// <summary> /// Submits this node's fee proposal. This methods must be called from within <see cref="asyncLockObject"/>. /// </summary> /// <param name="interopConversionRequestFee">The request we are currently processing.</param> /// <returns>The awaited task.</returns> private async Task SubmitProposalForInteropFeeForConversionRequestLockedAsync(InteropConversionRequestFee interopConversionRequestFee) { // Check if this node has already proposed its fee. if (!interopConversionRequestFee.FeeProposals.Any(p => p.PubKey == this.federationManager.CurrentFederationKey.PubKey.ToHex())) { if (!EstimateConversionTransactionFee(out ulong candidateFee)) { return; } interopConversionRequestFee.FeeProposals.Add(new InterOpFeeToMultisig() { BlockHeight = interopConversionRequestFee.BlockHeight, PubKey = this.federationManager.CurrentFederationKey.PubKey.ToHex(), FeeAmount = candidateFee }); this.interopRequestKeyValueStore.SaveValueJson(interopConversionRequestFee.RequestId, interopConversionRequestFee, true); this.logger.LogDebug($"Adding this node's proposal fee of {candidateFee} for conversion request id '{interopConversionRequestFee.RequestId}'."); } this.logger.LogDebug($"{interopConversionRequestFee.FeeProposals.Count} node(s) has proposed a fee for conversion request id '{interopConversionRequestFee.RequestId}'."); if (HasFeeProposalBeenConcluded(interopConversionRequestFee)) { // Only update the proposal state if it is ProposalInProgress and save it. if (interopConversionRequestFee.State == InteropFeeState.ProposalInProgress) { interopConversionRequestFee.State = InteropFeeState.AgreeanceInProgress; this.interopRequestKeyValueStore.SaveValueJson(interopConversionRequestFee.RequestId, interopConversionRequestFee, true); IEnumerable <long> values = interopConversionRequestFee.FeeProposals.Select(s => Convert.ToInt64(s.FeeAmount)); this.logger.LogDebug($"Proposal fee for request id '{interopConversionRequestFee.RequestId}' has concluded, average amount: {values.Average()}"); } } InterOpFeeToMultisig myProposal = interopConversionRequestFee.FeeProposals.First(p => p.PubKey == this.federationManager.CurrentFederationKey.PubKey.ToHex()); string signature = this.federationManager.CurrentFederationKey.SignMessage(interopConversionRequestFee.RequestId + myProposal.FeeAmount); await this.federatedPegBroadcaster.BroadcastAsync(FeeProposalPayload.Request(interopConversionRequestFee.RequestId, myProposal.FeeAmount, interopConversionRequestFee.BlockHeight, signature)).ConfigureAwait(false); }