コード例 #1
0
        /// <summary>
        /// Creates a new child context based on the current context instance.
        /// Includes an optional configuration method that can be immediately
        /// applied to the new context.  This method is used internally to create
        /// contexts for cloned clients and network method calls having custom
        /// configuration callbacks.
        /// </summary>
        private GossipContextStack CreateChildContext(Action <IContext>?configure)
        {
            var context = new GossipContextStack(_context);

            configure?.Invoke(context);
            return(context);
        }
コード例 #2
0
 internal TransactionBody(GossipContextStack context, TransactionID transactionId)
 {
     OnConstruction();
     TransactionID = transactionId;
     NodeAccountID = new AccountID(RequireInContext.Gateway(context));
     TransactionFee = (ulong)context.FeeLimit;
     TransactionValidDuration = new Proto.Duration(context.TransactionDuration);
     Memo = context.Memo ?? "";
 }
コード例 #3
0
 /// <summary>
 /// Internal Helper function used to wait for conesnsus regardless of the reported
 /// transaction outcome. We do not know if the transaction in question has come
 /// to consensus so we need to get the receipt first (and wait if necessary).
 /// The Receipt status returned does notmatter in this case.
 /// We may be retrieving a failed record (the status would not equal OK).
 private async Task WaitForConsensusReceipt(GossipContextStack context, TransactionID transactionId)
 {
     var query = new Query
     {
         TransactionGetReceipt = new TransactionGetReceiptQuery
         {
             TransactionID = transactionId
         }
     };
     await Transactions.ExecuteNetworkRequestWithRetryAsync(context, query, query.InstantiateNetworkRequestMethod, shouldRetry);
コード例 #4
0
        private static Action <IMessage> InstantiateOnSendingRequestHandler(this GossipContextStack context)
        {
            var handlers = context.GetAll <Action <IMessage> >(nameof(context.OnSendingRequest)).Where(h => h != null).ToArray();

            if (handlers.Length > 0)
            {
                return((IMessage request) => ExecuteHandlers(handlers, request));
            }
            else
            {
                return(NoOp);
            }
コード例 #5
0
 internal static TransactionID CreateTransactionID(this GossipContextStack context, Address payer)
 {
     var(seconds, nanos) = Epoch.UniqueSecondsAndNanos(context.AdjustForLocalClockDrift);
     return(new TransactionID
     {
         AccountID = new AccountID(payer),
         TransactionValidStart = new Proto.Timestamp
         {
             Seconds = seconds,
             Nanos = nanos
         }
     });
 }
コード例 #6
0
 /// <summary>
 /// Internal implementation of client creation.  Accounts for  newly created
 /// clients and cloning of clients alike.
 /// </summary>
 /// <param name="configure">
 /// The optional <see cref="IContext"/> callback method, passed in from public
 /// instantiation or a <see cref="Client.Clone(Action{IContext})"/> method call.
 /// </param>
 /// <param name="parent">
 /// The parent <see cref="GossipContextStack"/> if this creation is a result of a
 /// <see cref="Client.Clone(Action{IContext})"/> method call.
 /// </param>
 private Client(Action <IContext>?configure, GossipContextStack?parent)
 {
     if (parent is null)
     {
         // Create a Context with System Defaults
         // that are unreachable and can't be "Reset".
         parent = new GossipContextStack(null)
         {
             FeeLimit                 = 2_900_000_000,
             TransactionDuration      = TimeSpan.FromSeconds(120),
             RetryCount               = 5,
             RetryDelay               = TimeSpan.FromMilliseconds(200),
             AdjustForLocalClockDrift = false
         };
     }
     _context = new GossipContextStack(parent);
     configure?.Invoke(_context);
 }
コード例 #7
0
        internal static ISignatory GatherSignatories(this GossipContextStack context, params Signatory?[] extraSignatories)
        {
            var signatories      = new List <Signatory>(1 + extraSignatories.Length);
            var contextSignatory = context.Signatory;

            if (contextSignatory is not null)
            {
                signatories.Add(contextSignatory);
            }
            foreach (var extraSignatory in extraSignatories)
            {
                if (extraSignatory is not null)
                {
                    signatories.Add(extraSignatory);
                }
            }
            return(signatories.Count == 1 ?
                   signatories[0] :
                   new Signatory(signatories.ToArray()));
        }
コード例 #8
0
        /// <summary>
        /// Internal Helper function to retrieve receipt record provided by
        /// the network following network consensus regarding a query or transaction.
        /// </summary>
        private async Task <Proto.TransactionReceipt> GetReceiptAsync(GossipContextStack context, TransactionID transactionId)
        {
            var query = new Query
            {
                TransactionGetReceipt = new TransactionGetReceiptQuery
                {
                    TransactionID = transactionId
                }
            };
            var response = await Transactions.ExecuteNetworkRequestWithRetryAsync(context, query, getServerMethod, shouldRetry);

            var responseCode = response.TransactionGetReceipt.Header.NodeTransactionPrecheckCode;

            switch (responseCode)
            {
            case ResponseCodeEnum.Ok:
                break;

            case ResponseCodeEnum.Busy:
                throw new ConsensusException("Network failed to respond to request for a transaction receipt, it is too busy. It is possible the network may still reach concensus for this transaction.", Protobuf.FromTransactionId(transactionId), (ResponseCode)responseCode);

            case ResponseCodeEnum.Unknown:
            case ResponseCodeEnum.ReceiptNotFound:
                throw new TransactionException($"Network failed to return a transaction receipt, Status Code Returned: {responseCode}", Protobuf.FromTransactionId(transactionId), (ResponseCode)responseCode);
            }
            var status = response.TransactionGetReceipt.Receipt.Status;

            switch (status)
            {
            case ResponseCodeEnum.Unknown:
                throw new ConsensusException("Network failed to reach concensus within the configured retry time window, It is possible the network may still reach concensus for this transaction.", Protobuf.FromTransactionId(transactionId), (ResponseCode)status);

            case ResponseCodeEnum.TransactionExpired:
                throw new ConsensusException("Network failed to reach concensus before transaction request expired.", Protobuf.FromTransactionId(transactionId), (ResponseCode)status);

            case ResponseCodeEnum.RecordNotFound:
                throw new ConsensusException("Network failed to find a receipt for given transaction.", Protobuf.FromTransactionId(transactionId), (ResponseCode)status);

            default:
                return(response.TransactionGetReceipt.Receipt);
            }
コード例 #9
0
        internal static TransactionID GetOrCreateTransactionID(this GossipContextStack context)
        {
            var preExistingTransaction = context.Transaction;

            if (preExistingTransaction is null)
            {
                var payer = context.Payer;
                if (payer is null)
                {
                    throw new InvalidOperationException("The Payer address has not been configured. Please check that 'Payer' is set in the Client context.");
                }
                return(CreateTransactionID(context, payer));
            }
            else if (preExistingTransaction.Pending)
            {
                throw new ArgumentException("Can not set the context's Transaction ID's Pending field of a transaction to true.", nameof(context.Transaction));
            }
            else
            {
                return(new TransactionID(preExistingTransaction));
            }
        }
コード例 #10
0
ファイル: Transactions.cs プロジェクト: sannynguyen/Hashgraph
        internal static TransactionID GetOrCreateTransactionID(GossipContextStack context)
        {
            var preExistingTransaction = context.Transaction;

            if (preExistingTransaction is null)
            {
                var(seconds, nanos) = Epoch.UniqueSecondsAndNanos(context.AdjustForLocalClockDrift);
                return(new TransactionID
                {
                    AccountID = new AccountID(RequireInContext.Payer(context)),
                    TransactionValidStart = new Proto.Timestamp
                    {
                        Seconds = seconds,
                        Nanos = nanos
                    }
                });
            }
            else
            {
                return(new TransactionID(preExistingTransaction));
            }
        }
コード例 #11
0
 /// <summary>
 /// Internal Helper function used to wait for conesnsus regardless of the reported
 /// transaction outcome. We do not know if the transaction in question has come
 /// to consensus so we need to get the receipt first (and wait if necessary).
 /// The Receipt status returned does notmatter in this case.
 /// We may be retrieving a failed record (the status would not equal OK).
 private async Task WaitForConsensusReceipt(GossipContextStack context, TransactionID transactionId)
 {
     var query = new TransactionGetReceiptQuery(transactionId, false, false) as INetworkQuery;
     await context.ExecuteNetworkRequestWithRetryAsync(query.CreateEnvelope(), query.InstantiateNetworkRequestMethod, shouldRetry).ConfigureAwait(false);
コード例 #12
0
        internal static async Task <TResponse> ExecuteNetworkRequestWithRetryAsync <TRequest, TResponse>(this GossipContextStack context, TRequest request, Func <Channel, Func <TRequest, Metadata?, DateTime?, CancellationToken, AsyncUnaryCall <TResponse> > > instantiateRequestMethod, Func <TResponse, bool> shouldRetryRequest) where TRequest : IMessage where TResponse : IMessage
        {
            try
            {
                var retryCount                     = 0;
                var maxRetries                     = context.RetryCount;
                var retryDelay                     = context.RetryDelay;
                var callOnSendingHandlers          = InstantiateOnSendingRequestHandler(context);
                var callOnResponseReceivedHandlers = InstantiateOnResponseReceivedHandler(context);
                var sendRequest                    = instantiateRequestMethod(context.GetChannel());
                callOnSendingHandlers(request);
                for (; retryCount < maxRetries; retryCount++)
                {
                    try
                    {
                        var tenativeResponse = await sendRequest(request, null, null, default);

                        callOnResponseReceivedHandlers(retryCount, tenativeResponse);
                        if (!shouldRetryRequest(tenativeResponse))
                        {
                            return(tenativeResponse);
                        }
                    }
                    catch (RpcException rpcex) when(rpcex.StatusCode == StatusCode.Unavailable || rpcex.StatusCode == StatusCode.Unknown)
                    {
                        var channel = context.GetChannel();
                        var message = channel.State == ChannelState.Connecting ?
                                      $"Unable to communicate with network node {channel.ResolvedTarget}, it may be down or not reachable." :
                                      $"Unable to communicate with network node {channel.ResolvedTarget}: {rpcex.Status}";

                        callOnResponseReceivedHandlers(retryCount, new StringValue {
                            Value = message
                        });

                        // If this was a transaction, it may have actully successfully been processed, in which case
                        // the receipt will already be in the system.  Check to see if it is there.
                        if (request is Transaction transaction)
                        {
                            await Task.Delay(retryDelay *retryCount).ConfigureAwait(false);

                            var receiptResponse = await CheckForReceipt(transaction).ConfigureAwait(false);

                            callOnResponseReceivedHandlers(retryCount, receiptResponse);
                            if (receiptResponse.NodeTransactionPrecheckCode != ResponseCodeEnum.ReceiptNotFound &&
                                receiptResponse is TResponse tenativeResponse &&
                                !shouldRetryRequest(tenativeResponse))
                            {
                                return(tenativeResponse);
                            }
                        }
                    }
                    await Task.Delay(retryDelay *(retryCount + 1)).ConfigureAwait(false);
                }
                var finalResponse = await sendRequest(request, null, null, default);

                callOnResponseReceivedHandlers(maxRetries, finalResponse);
                return(finalResponse);

                async Task <TransactionResponse> CheckForReceipt(Transaction transaction)
                {
                    // In the case we submitted a transaction, the receipt may actually
                    // be in the system.  Unpacking the transaction is not necessarily efficient,
                    // however we are here due to edge case error condition due to poor network
                    // performance or grpc connection issues already.
                    if (transaction != null)
                    {
                        var signedTransaction = SignedTransaction.Parser.ParseFrom(transaction.SignedTransactionBytes);
                        var transactionBody   = TransactionBody.Parser.ParseFrom(signedTransaction.BodyBytes);
                        var transactionId     = transactionBody.TransactionID;
                        var query             = new Query
                        {
                            TransactionGetReceipt = new TransactionGetReceiptQuery
                            {
                                TransactionID = transactionId
                            }
                        };
                        for (; retryCount < maxRetries; retryCount++)
                        {
                            try
                            {
                                var client  = new CryptoService.CryptoServiceClient(context.GetChannel());
                                var receipt = await client.getTransactionReceiptsAsync(query);

                                return(new TransactionResponse {
                                    NodeTransactionPrecheckCode = receipt.TransactionGetReceipt.Header.NodeTransactionPrecheckCode
                                });
                            }
                            catch (RpcException rpcex) when(rpcex.StatusCode == StatusCode.Unavailable)
                            {
                                var channel = context.GetChannel();
                                var message = channel.State == ChannelState.Connecting ?
                                              $"Unable to communicate with network node {channel.ResolvedTarget}, it may be down or not reachable." :
                                              $"Unable to communicate with network node {channel.ResolvedTarget}: {rpcex.Status}";

                                callOnResponseReceivedHandlers(retryCount, new StringValue {
                                    Value = message
                                });
                            }
                            await Task.Delay(retryDelay *(retryCount + 1)).ConfigureAwait(false);
                        }
                    }
                    return(new TransactionResponse {
                        NodeTransactionPrecheckCode = ResponseCodeEnum.Unknown
                    });
                }
            }
            catch (RpcException rpcex)
            {
                var channel = context.GetChannel();
                var message = rpcex.StatusCode == StatusCode.Unavailable && channel.State == ChannelState.Connecting ?
                              $"Unable to communicate with network node {channel.ResolvedTarget}, it may be down or not reachable." :
                              $"Unable to communicate with network node {channel.ResolvedTarget}: {rpcex.Status}";
                throw new PrecheckException(message, TxId.None, ResponseCode.RpcError, 0, rpcex);
            }
        }
コード例 #13
0
        internal static Task <TResponse> ExecuteSignedRequestWithRetryImplementationAsync <TRequest, TResponse>(this GossipContextStack context, TRequest request, Func <Channel, Func <TRequest, Metadata?, DateTime?, CancellationToken, AsyncUnaryCall <TResponse> > > instantiateRequestMethod, Func <TResponse, ResponseCodeEnum> getResponseCode) where TRequest : IMessage where TResponse : IMessage
        {
            var trackTimeDrift  = context.AdjustForLocalClockDrift && context.Transaction is null;
            var startingInstant = trackTimeDrift ? Epoch.UniqueClockNanos() : 0;

            return(ExecuteNetworkRequestWithRetryAsync(context, request, instantiateRequestMethod, shouldRetryRequest));

            bool shouldRetryRequest(TResponse response)
            {
                var code = getResponseCode(response);

                if (trackTimeDrift && code == ResponseCodeEnum.InvalidTransactionStart)
                {
                    Epoch.AddToClockDrift(Epoch.UniqueClockNanos() - startingInstant);
                }
                return
                    (code == ResponseCodeEnum.Busy ||
                     code == ResponseCodeEnum.InvalidTransactionStart);
            }
        }
コード例 #14
0
 internal async Task<TransactionResponse> SignAndSubmitWithRetryAsync(ISignatory signatory, GossipContextStack context)
 {
     var request = await SignAsync(signatory, context.SignaturePrefixTrimLimit);
     return await Transactions.ExecuteSignedRequestWithRetryImplementationAsync(context, request, InstantiateNetworkRequestMethod, getResponseCode);
コード例 #15
0
 internal async Task<TransactionReceipt> SignAndExecuteWithRetryAsync(ISignatory signatory, GossipContextStack context)
 {
     var precheck = await SignAndSubmitWithRetryAsync(signatory, context);
     ValidateResult.PreCheck(TransactionID, precheck);
     var receipt = await Transactions.GetReceiptAsync(context, TransactionID);
     return receipt;
 }